Compare commits

...

274 Commits

Author SHA1 Message Date
Lioncash
28ec921d0d core/CMakeLists: Make all web_service-related libraries private
Now that all external dependencies are hidden, we can remove
json-headers from the publically linked libraries, as the use of this
library is now completely hidden from external users of the web_service
library. We can also make the web_services library private as well,
considering it's not a requirement. If a library needs to link in
web_service, it should be done explicitly -- not via indirect linking.
2018-10-10 22:29:39 -04:00
Lioncash
183a664405 web_backend: Make Client use the PImpl idiom
Like with TelemetryJson, we can make the implementation details private
and avoid the need to expose httplib to external libraries that need to
use the Client class.
2018-10-10 22:29:35 -04:00
Lioncash
a7725d354c telemetry_json: Use the PImpl idiom to avoid unnecessary dependency exposure
Users of the web_service library shouldn't need to care about an
external library like json.h. However, given it's exposed in our
interface, this requires that other libraries publicly link in the JSON
library. We can do better.

By using the PImpl idiom, we can hide this dependency in the cpp file
and remove the need to link that library in altogether.
2018-10-10 21:10:36 -04:00
Lioncash
c422f146ee telemetry_json: Add missing override specifier to the destructor of TelemetryJson 2018-10-10 21:00:39 -04:00
Lioncash
881bb2295d telemetry_json: Take std::string parameters by value
Taking them by const reference isn't advisable here, because it means
the std::move calls were doing nothing and we were always copying the
std::string instances.
2018-10-10 20:59:28 -04:00
Lioncash
a34e5e51d8 telemetry_json: Remove unnecessary includes
Removes unused includes. Also rectifies a missing <chrono> include.
2018-10-10 20:57:31 -04:00
Lioncash
6e6ce2ce39 core/CMakeLists: Use target_compile_definitions instead of add_definitions for specifying ENABLE_WEB_SERVICE
Avoids introducing the definition to the whole directory space and
localizes it to being added to the library that needs it.
2018-10-10 20:54:02 -04:00
bunnei
03ec936ca0 Merge pull request #1460 from FernandoS27/scissor_test
Implemented Scissor Testing
2018-10-10 12:04:10 -04:00
bunnei
ee1b204749 Merge pull request #1425 from ReinUsesLisp/geometry-shaders
gl_shader_decompiler: Implement geometry shaders
2018-10-10 11:51:29 -04:00
bunnei
68b3d8b7a9 Merge pull request #1469 from lioncash/ptr
kernel/thread: Use a regular pointer for the owner/current process
2018-10-10 10:34:20 -04:00
Lioncash
5c0408596f kernel/thread: Use a regular pointer for the owner/current process
There's no real need to use a shared pointer in these cases, and only
makes object management more fragile in terms of how easy it would be to
introduce cycles. Instead, just do the simple thing of using a regular
pointer. Much of this is just a hold-over from citra anyways.

It also doesn't make sense from a behavioral point of view for a
process' thread to prolong the lifetime of the process itself (the
process is supposed to own the thread, not the other way around).
2018-10-10 02:04:55 -04:00
bunnei
5461b21c7a Merge pull request #1461 from lioncash/warn
ips_layer: Silence truncation and conversion warnings
2018-10-09 22:30:01 -04:00
bunnei
3ac874c32e Merge pull request #1464 from lioncash/unique
patch_manager: Return a std::unique_ptr from ParseControlNCA() and GetControlMetadata() instead of a std::shared_ptr
2018-10-09 22:29:39 -04:00
bunnei
bc6939beaa Merge pull request #1466 from lioncash/unused
gl_shader_decompiler: Remove unused variables in TMML's implementation
2018-10-09 19:03:06 -04:00
bunnei
0b3d4db98b Merge pull request #1463 from FearlessTobi/port-4310
Port citra-emu/citra#4310: "Handle touch input"
2018-10-09 19:02:41 -04:00
bunnei
fe16905de1 Merge pull request #1459 from ogniK5377/break
svcBreak, Signalling to the debugger should not kill execution
2018-10-09 16:57:37 -04:00
bunnei
89939be9e6 Merge pull request #1465 from lioncash/telemetry
telemetry_session: Minor miscellaneous changes
2018-10-09 16:56:56 -04:00
bunnei
141a0d9386 Merge pull request #1462 from lioncash/move
ips_layer: Minor miscellaneous changes
2018-10-09 16:56:32 -04:00
bunnei
6aab309e41 Merge pull request #1455 from ogniK5377/smo-softlockfix
Fixed smo softlock due to incorrect effect state updating
2018-10-09 16:56:11 -04:00
Lioncash
6e27c5d4d1 gl_shader_decompiler: Remove unused variables in TMML's implementation
Given "y" isn't always used, but "x" is, we can rearrange this to avoid
unused variable warnings by changing the names of op_a and op_b
2018-10-09 15:44:37 -04:00
Lioncash
e3b4d31f4e telemetry_session: Remove doxygen comment for a non-existent parameter
There's no "func" parameter, so this can just be removed.
2018-10-09 14:52:10 -04:00
Lioncash
8aa4889e76 telemetry_session: Add missing includes
Prevents potential compilation issues in the future by including missing
headers for certain functions and types.
2018-10-09 14:51:39 -04:00
Lioncash
1964f4bbb3 telemetry_session: Remove unimplemented FinalizeAsyncJob prototype
This isn't implemented anywhere, so it can just be removed.
2018-10-09 14:46:31 -04:00
Lioncash
8723cc8798 telemetry_session: Use a std::array in GenerateTelemetryId()
We don't need to potentially heap-allocate a std::string instance here,
given the data is known ahead of time. We can just place it within an
array and pass this to the mbedtls functions.
2018-10-09 14:46:26 -04:00
Lioncash
6636f3ff47 patch_manager: Return a std::unique_ptr from ParseControlNCA() and GetControlMetadata() instead of a std::shared_ptr
Neither of these functions require the use of shared ownership of the
returned pointer. This makes it more difficult to create reference
cycles with, and makes the interface more generic, as std::shared_ptr
instances can be created from a std::unique_ptr, but the vice-versa
isn't possible. This also alters relevant functions to take NCA
arguments by const reference rather than a const reference to a
std::shared_ptr. These functions don't alter the ownership of the memory
used by the NCA instance, so we can make the interface more generic by
not assuming anything about the type of smart pointer the NCA is
contained within and make it the caller's responsibility to ensure the
supplied NCA is valid.
2018-10-09 14:38:03 -04:00
NeatNit
4f24343f32 implemented touch in Qt and SDL
change TouchToPixelPos to return std::pair<int, int>

static_cast (SDL)

various minor style and code improvements

style - PascalCase for function names

made touch events private

const pointer arg in touch events

make TouchToPixelPos a const member function

did I do this right?

braces on barely-multiline if

remove question comment (confirmed in Discord)

fixed consts

remove unused parameter from TouchEndEvent

DRY - High-DPI scaled touch put in separate function

also fixes a bug where if you start touching (with either mouse or touchscreen) and drag the mouse to the LEFT of the emulator window, the touch point jumps to the RIGHT side of the touchscreen; draggin to above the window would make it jump to the bottom.

implicit conversion from QPoint to QPointF, apparently

I have no idea what const even means but I'll put it here anyway

remove unused or used-once variables

make touch scaling functions const, and put their implementations together

removed unused FingerID parameters

QTouchEvent forward declaration; add comment to TouchBegin that was lost in an edit

better DRY in SDL

To do -> TODO(NeatNit)

remove unused include
2018-10-09 20:26:57 +02:00
Lioncash
465175cdf5 ips_layer: Avoid constructing std::vector instances where not necessary
We can just compare the existing std::vector instance with a constexpr
std::array containing the desired match. This is lighter resource-wise,
as we don't need to allocate on the heap.
2018-10-09 14:10:22 -04:00
Lioncash
9ff743bc0a ips_layer: Remove unnecessary explicit std::pair constructor in std::array
Makes the layout of the array consistent, by making all elements match,
instead of special-casing the first one.
2018-10-09 14:10:22 -04:00
Lioncash
f7d2889fb4 ips_layer: Add missing includes
Adds missing includes to prevent potential compilation issues in the
future. Also moves the definition of a struct into the cpp file, so that
some includes don't need to be introduced within the header.
2018-10-09 14:10:13 -04:00
Lioncash
93ac8d0fea ips_layer: std::move data within PatchIPS() and Apply()
We don't need to make a copy of the read data, so we can std::move it
into the make_shared call here.
2018-10-09 14:06:44 -04:00
Lioncash
567e818440 ips_layer: Silence truncation and conversion warnings
Makes type conversions explicit to avoid compiler warnings.
2018-10-09 13:18:23 -04:00
FernandoS27
be97fc884d Implement Scissor Test 2018-10-08 21:36:23 -04:00
David Marcec
f5631e78d1 Added bitfield instead of manually checking if the bit is set 2018-10-09 12:11:14 +11:00
FernandoS27
30ff42b8cc Assert Scissor tests 2018-10-08 20:49:36 -04:00
David Marcec
a47c1c77e6 EffectOutStatus padding is now in hex 2018-10-09 11:20:54 +11:00
David Marcec
af3ba94b2a Actual kill execution when the bit isn't set, not the other way around 2018-10-09 11:14:48 +11:00
David Marcec
c50f66a8eb svcBreak, Signalling to the debugger should not kill execution
When loading NROs, svcBreak is called to signal to the debugger that a new "module" is loaded. As no debugger is technically attached we shouldn't be killing the programs execution.
2018-10-09 11:10:30 +11:00
bunnei
561d79e034 Merge pull request #1423 from DarkLordZach/romfs-file-exts
fsmitm_romfsbuild: Add support for stubbing and IPS patches in LFS
2018-10-08 12:31:27 -04:00
bunnei
6b48ba5271 Merge pull request #1424 from DarkLordZach/ips-witch
ips_layer: Add support for IPSwitch executable patches
2018-10-08 12:30:33 -04:00
bunnei
fd891ee9c0 Merge pull request #1456 from ogniK5377/aoc-u-fixups
Fixed assertion due to CountAddOnContent & Casting warnings
2018-10-08 01:21:05 -04:00
bunnei
3f1f82a8c4 Merge pull request #1457 from ogniK5377/unmap-buffer
Unmapping an unmapped buffer should succeed
2018-10-08 01:20:18 -04:00
bunnei
ae982a9bdf Merge pull request #1419 from DarkLordZach/homebrew-args
nso/nro: Add support for passing command-line arguments to executable
2018-10-08 01:19:39 -04:00
David Marcec
c5c184246d Unmapping an unmapped buffer should succeed
Hardware tests show that trying to unmap an unmapped buffer already should always succeed. Hardware test was tested up to 32 iterations of attempting to unmap
2018-10-08 13:26:48 +11:00
ReinUsesLisp
7c2d6ef210 gl_shader_decompiler: Move position varying location from 15 to 0 and apply an offset 2018-10-07 17:36:00 -03:00
ReinUsesLisp
ee4d538850 gl_shader_decompiler: Implement geometry shaders 2018-10-07 17:36:00 -03:00
ReinUsesLisp
4d0c682468 video_core: Allow LabelGLObject to use extra info on any object 2018-10-07 17:27:49 -03:00
Zach Hilman
f945e9767c nso/nro: Use default allocation size for arg_data 2018-10-07 14:32:33 -04:00
Zach Hilman
081f5c1dbf cmd: Support passing game arguments from command line
Uses -p (--program) and following string as args.
2018-10-07 14:32:32 -04:00
Zach Hilman
8bbc12b9c2 qt: Add UI option to configure arguments 2018-10-07 14:32:06 -04:00
Zach Hilman
95dff555a4 settings: Add program_args string setting 2018-10-07 14:32:05 -04:00
Zach Hilman
e09505ff61 nso/nro: Add NSO arguments structure to data section
Only added if arguments string is non-empty and a pass is requested by loader.
2018-10-07 14:30:15 -04:00
David Marcec
fa3f3cd07f Fixed assertion due to CountAddOnContent
Word count should be 3 since we're pushing a result code and a u32.

Also fixed up compiler warnings due to casting
2018-10-08 00:25:46 +11:00
bunnei
6e4d2e672d Merge pull request #1396 from DarkLordZach/packed-updates
loader: Add support for packed updates
2018-10-06 23:58:24 -04:00
David Marcec
ceef334c1c Fixups for softlock 2018-10-07 14:25:39 +11:00
David Marcec
2534af040e Fixed missing return
Softlock explanation:
after effects are initialized in smo, nothing actually changes the state. It expects the state to always be initialized. With the previous testing, updating the states much like how we handle the memory pools continue to have the softlock(which is why I said it probably wasn't effects) after further examination it seems like effects need to be initialized but the state remains unchanged until further notice. For now, assertions are added for the aux buffers to see if they update, unable to check as I haven't gotten smo to actually update them yet.
2018-10-07 14:19:55 +11:00
bunnei
2c0b0ad50d Merge pull request #1446 from bunnei/fast_fermi_copy
gl_rasterizer: Implement accelerated Fermi2D copies.
2018-10-06 23:18:52 -04:00
bunnei
1cc5e6e9bc Merge pull request #1437 from FernandoS27/tex-mode2
Implemented Depth Compare, Shadow Samplers and Texture Processing Modes for TEXS and TLDS
2018-10-06 23:14:27 -04:00
David Marcec
2de52e3af6 Fixed smo softlock 2018-10-07 14:14:09 +11:00
bunnei
6f420a40cf Merge pull request #1453 from FearlessTobi/port-4311
Port citra-emu/citra#4311: "Remove "#" in the version number"
2018-10-06 23:12:58 -04:00
bunnei
44a3baf410 Merge pull request #1451 from FearlessTobi/port-4140
Port citra-emu/citra#4140: "misc input tab improvements"
2018-10-06 23:11:32 -04:00
Mat M
e92251795a Merge pull request #1450 from FearlessTobi/port-4312
Port citra-emu/citra#4312: "Update fmt to 5.2.1"
2018-10-06 22:26:06 -04:00
bunnei
450c0a5adf Merge pull request #1448 from ogniK5377/frontend-access
Ported #4296 from citra
2018-10-06 22:25:29 -04:00
Mat M
72a804b149 Merge pull request #1454 from ReinUsesLisp/fixup-draw
gl_rasterizer: Fixup undefined behaviour in SetupDraw
2018-10-06 22:24:08 -04:00
ReinUsesLisp
0ecd181cca gl_rasterizer: Fixup undefined behaviour in SetupDraw 2018-10-06 23:22:48 -03:00
bunnei
2fbb20b2b5 yuzu/yuzu_cmd: Add checks for required extension ARB_copy_image. 2018-10-06 12:06:40 -04:00
FernandoS27
752faff2bc Implemented Depth Compare and Shadow Samplers 2018-10-06 11:27:54 -04:00
Mat M
6d4f5b9673 Merge pull request #1452 from FearlessTobi/port-4313
Port citra-emu/citra#4313: "CONTRIBUTING.md - remove note about casting numeric types"
2018-10-06 10:11:35 -04:00
fearlessTobi
8e6311bfd2 Remove "#" in the version number
So that people can stop using it in issue/pr comments and randomly link some other issue/pr unintentionally.
2018-10-06 15:51:37 +02:00
NeatNit
d210170f36 CONTRIBUTING.md - remove note about casting numeric types
Apparently it's not true: https://github.com/citra-emu/citra/pull/4310#discussion_r223174725
2018-10-06 15:47:38 +02:00
zhupengfei
690f326613 citra_qt/configuration: misc input tab improvements
* Added a context menu on the buttons including Clear & Restore Default

* Allow clearing (unsetting) inputs. Added a Clear All button

* Allow restoring a single input to default (instead of all)
2018-10-06 15:43:49 +02:00
Weiyi Wang
a45b86f0fb Update fmt to 5.2.1
It seems to fix msvc warnings
2018-10-06 15:39:31 +02:00
David Marcec
612ce89eca Added forward define for ServerPort 2018-10-06 17:47:33 +10:00
bunnei
9aec85d39c fermi_2d: Implement simple copies with AccelerateSurfaceCopy. 2018-10-06 03:20:04 -04:00
bunnei
011cf77796 gl_rasterizer: Add rasterizer cache code to handle accerated fermi copies. 2018-10-06 03:20:04 -04:00
bunnei
749aef3dd0 gl_rasterizer_cache: Implement a simpler surface copy using glCopyImageSubData. 2018-10-06 03:20:04 -04:00
bunnei
6cc7656e81 Merge pull request #1449 from lioncash/link
qt: Update telemetry links
2018-10-06 03:18:13 -04:00
Lioncash
efd956e6ff qt: Update telemetry links
These were pointing to a non-existent webpage.
2018-10-06 03:16:39 -04:00
David Marcec
f84b9ed4e8 Ported #4296 from citra
This will allow us to easily remove the use of "NFC" in "System"
2018-10-06 16:49:01 +10:00
bunnei
b8b90ce6e6 Merge pull request #1332 from FearlessTobi/port-web-backend
Port web_service from Citra
2018-10-06 02:43:09 -04:00
bunnei
095c8d999b Merge pull request #1447 from lioncash/mutex
kernel/mutex: Amend behavior of TransferMutexOwnership()
2018-10-06 01:38:34 -04:00
Lioncash
6ddf8f34db kernel/mutex: Amend behavior of TransferMutexOwnership()
This was the result of a typo accidentally introduced in
e51d715700. This restores the previous
correct behavior.

The behavior with the reference was incorrect and would cause some games
to fail to boot.
2018-10-06 01:13:02 -04:00
bunnei
e6ee31a8e9 Merge pull request #1440 from lioncash/array
ui_settings: Place definition of the theme array within the cpp file
2018-10-05 22:54:01 -04:00
bunnei
d3bfb102d8 Merge pull request #1438 from ReinUsesLisp/quads
gl_rasterizer: Implement quads topology
2018-10-05 22:53:22 -04:00
bunnei
d43769f93f Merge pull request #1445 from lioncash/sched
thread: Make the scheduler pointer a regular pointer
2018-10-05 22:33:03 -04:00
balika011
1a5d6de0d4 thread: Make the scheduler pointer a regular pointer
Conceptually, it doesn't make sense for a thread to be able to persist
the lifetime of a scheduler. A scheduler should be taking care of the
threads; the threads should not be taking care of the scheduler.

If the threads outlive the scheduler (or we simply don't actually
terminate/shutdown the threads), then it should be considered a bug
that we need to fix.

Attributing this to balika011, as they opened #1317 to attempt to fix
this in a similar way, but my refactoring of the kernel code caused
quite a few conflicts.
2018-10-05 14:53:01 -04:00
bunnei
e51d715700 Merge pull request #1439 from lioncash/thread
kernel/thread: Make all instance variables private
2018-10-05 13:41:54 -04:00
bunnei
fe292573de Merge pull request #1442 from lioncash/format
text_formatter: Avoid unnecessary string temporary creation in PrintMessage()
2018-10-05 10:48:58 -04:00
Zach Hilman
38c2ac95af romfs_factory: Extract packed update setter to new function 2018-10-05 08:53:51 -04:00
Zach Hilman
5acaeb04c4 patch_manager: Add support for NSP packed updates
Reads as Update (NSP) in add-ons
2018-10-05 08:48:44 -04:00
Zach Hilman
cf7aba4817 game_list: Add XCI update versioning to game list 2018-10-05 08:47:55 -04:00
Zach Hilman
d79d4fd764 patch_manager: Add support for packed updates
Will prefer any installed update over the packed version.
2018-10-05 08:47:24 -04:00
Zach Hilman
5045748829 loader: Add getter for packed update
Reads the update included with the game if it has one and adds the new ErrorNoPackedUpdate status.
2018-10-05 08:46:31 -04:00
Zach Hilman
e948fbf5d0 loader: Add ReadRomFSIVFCOffset to NSP, XCI, and NAX loaders
Fixes errors with certain updates
2018-10-05 08:46:31 -04:00
Lioncash
6f16826260 text_formatter: Avoid unnecessary string temporary creation in PrintMessage()
operator+ for std::string creates an entirely new string, which is kind
of unnecessary here if we just want to append a null terminator to the
existing one.

Reduces the total amount of potential allocations that need to be done
in the logging path.
2018-10-04 23:55:53 -04:00
Zach Hilman
d0e6b93695 patch_manager: Avoid romfs_ext requirement for patching 2018-10-04 14:09:11 -04:00
Zach Hilman
c1e069c066 fsmitm_romfsbuild: Extract stubs and IPS to romfs_ext dir 2018-10-04 12:29:20 -04:00
Zach Hilman
bc4bec8a60 fsmitm_romfsbuild: Add support for stubbing and IPS patches in LFS 2018-10-04 12:29:14 -04:00
Zach Hilman
110d578470 ips_layer: Fix inaccuracies with comments and flags
Specifically bugs/crashes that arise when putting them in positions that are legal but not typical, such as midline, between patch data, or between patch records.
2018-10-04 12:23:27 -04:00
Zach Hilman
70bd2bb1d3 ips_layer: Deduplicate resource usage 2018-10-04 11:34:36 -04:00
Zach Hilman
9669cdb710 ips_layer: Add support for escape sequences and midline comments
More accurately follows IPSwitch specification.
2018-10-04 11:34:30 -04:00
Zach Hilman
8886f2e55e patch_manager: Add support for IPSwitch format patches 2018-10-04 11:34:06 -04:00
Zach Hilman
306739c2c4 ips_layer: Add IPSwitchCompiler to process IPSwitch format 2018-10-04 11:32:10 -04:00
Zach Hilman
f62227aa95 hex_util: Add HexVectorToString and HexStringToVector
Converts between bytes and strings when the size is not known at compile time.
2018-10-04 11:32:04 -04:00
Lioncash
30dfd89126 ui_settings: Place definition of the theme array within the cpp file
Placing the array wholesale into the header places a copy of the whole
array into every translation unit that uses the data, which is wasteful.
Particularly given that this array is referenced from three different
translation units.

This also changes the array to contain pairs of const char*, rather than
QString instances. This way, the string data is able to be fixed into
the read-only segment of the program, as well as eliminate static
constructors/heap allocation immediately on program start.
2018-10-04 09:43:51 -04:00
bunnei
f85f2b3728 Merge pull request #1415 from DarkLordZach/ips
file_sys: Add support for loading IPS patches
2018-10-04 09:42:37 -04:00
Lioncash
baed7e1fba kernel/thread: Make all instance variables private
Many of the member variables of the thread class aren't even used
outside of the class itself, so there's no need to make those variables
public. This change follows in the steps of the previous changes that
made other kernel types' members private.

The main motivation behind this is that the Thread class will likely
change in the future as emulation becomes more accurate, and letting
random bits of the emulator access data members of the Thread class
directly makes it a pain to shuffle around and/or modify internals.
Having all data members public like this also makes it difficult to
reason about certain bits of behavior without first verifying what parts
of the core actually use them.

Everything being public also generally follows the tendency for changes
to be introduced in completely different translation units that would
otherwise be better introduced as an addition to the Thread class'
public interface.
2018-10-04 00:14:15 -04:00
ReinUsesLisp
3e2380327a gl_rasterizer: Implement quads topology 2018-10-04 00:03:44 -03:00
bunnei
cf3a6dd4a1 Merge pull request #1434 from DarkLordZach/dlc-edge-case
aoc_u: Fix edge case with DLC that causes breaks
2018-10-03 21:39:23 -04:00
bunnei
af672d8abf Merge pull request #1428 from lioncash/qt
configure_graphics: Make functions internally linked where applicable
2018-10-03 21:38:29 -04:00
bunnei
839dbd9710 Merge pull request #1431 from lioncash/audio
configure_audio: Minor cleanup-related changes
2018-10-03 19:14:22 -04:00
bunnei
39f08e551d Merge pull request #1433 from lioncash/fs
services/fsp_srv: Amend service function table
2018-10-03 19:13:57 -04:00
bunnei
5ae5610c13 Merge pull request #1429 from lioncash/translate
configure_input: Make analog mapping strings translatable
2018-10-03 19:13:20 -04:00
bunnei
15b2e2ec13 Merge pull request #1436 from lioncash/view
submission_package: Cleanup and bug fixes
2018-10-03 19:05:19 -04:00
bunnei
e7e347a828 Merge pull request #1432 from lioncash/lbl
service/lbl: Update service function table
2018-10-03 18:45:15 -04:00
bunnei
137d43fa2f Merge pull request #1426 from FearlessTobi/port-4253
Port citra-emu/citra#4253: "common/string_util cleanup"
2018-10-03 18:44:51 -04:00
bunnei
8679934693 Merge pull request #1435 from lioncash/xci
card_image: Ensure program_nca_status is always initialized
2018-10-03 18:44:28 -04:00
FernandoS27
f664437ae8 Implemented Texture Processing Modes in TEXS and TLDS 2018-10-03 08:41:12 -04:00
Lioncash
024eec02a5 submission_package: Avoid dangling std::string_view within SetTicketKeys()
GetName() returns a std::string by value, not by reference, so after the
std::string_view is constructed, it's not well defined to actually
execute any member functions of std::string_view that attempt to access
the data, as the std::string has already been destroyed. Instead, we can
just use a std::string and erase the last four characters.
2018-10-03 02:13:51 -04:00
Lioncash
dade709f63 submission_package: Correct location of null check within SetTicketKeys()
If a ticket file was ever a null pointer, we'd cause a null pointer
dereference, as we were calling GetExtension() on the pointer instance.
2018-10-03 02:06:30 -04:00
Lioncash
02841052aa submission_package: Use std::string's rfind() when looking for the extension in InitializeExeFSAndRomFS()
When searching for a file extension, it's generally preferable to begin
the search at the end of the string rather than the beginning, as the
whole string isn't going to be walked just to check for something at the
end of it.
2018-10-03 01:51:01 -04:00
Lioncash
37ee05f7c0 submission_package: Ensure the 'extracted' member variable is always initialized
If an error occurs when constructing the PartitionFilesystem instance,
the constructor would be exited early, which wouldn't initialize the
extracted data member, making it possible for other code to perform an
uninitialized read by calling the public IsExtractedType() member
function. This prevents that.
2018-10-03 01:47:34 -04:00
Lioncash
ccf0a9cb38 submission_package: Move ExeFS and RomFS initialization to its own function
Like the other two bits of factored out code, this can also be put
within its own function. We can also modify the code so that it accepts
a const reference to a std::vector of files, this way, we can
deduplicate the file retrieval.

Now the constructor for NSP isn't a combination of multiple behaviors in
one spot. It's nice and separate.
2018-10-03 01:46:07 -04:00
Lioncash
fd312abedd submission_package: Move NCA reading code to its own function
This too, is completely separate behavior from what is in the
constructor, so we can move this to its own isolated function to keep
everything self-contained.
2018-10-03 01:35:41 -04:00
Lioncash
4f18d35888 submission_package: Move ticket key setting to its own function
This behavior is entirely independent of the surrounding code, so it can
be put in its own function to keep the behavior separate.
2018-10-03 01:20:57 -04:00
Lioncash
0d83f8f255 submission_package: Invert conditionals within NSP's constructor to reduce nesting
We can use early continues here to reduce the amount of nesting.
2018-10-03 01:11:16 -04:00
Lioncash
cf463a9b67 card_image: Ensure program_nca_status is always initialized
If any of the error paths before the NCA retrieval are taken, it'll
result in program_nca_status being left in an inconsistent state. So we
initialize it by default with a value indicating an error.
2018-10-02 23:16:05 -04:00
Zach Hilman
9aaf1c0df8 aoc_u: Fix edge case with DLC that causes breaks
In some games (Splatoon 2 and Splatoon 2 Splatfest World Premiere, notably), pass offset=0 and count=2047 into the ListAddOnContent method which should return all DLCs for the current title. The (presumably) intended behavior is to successfully return a empty array but because of a < v. <= in an if statement, a failure error code was returned causing these games to svcBreak. This fixes that if statement.
2018-10-02 21:56:32 -04:00
Lioncash
cba78dc70a services/fsp_srv: Amend service function table
Adds new functions that have been given names to the table. Information
is based off what is provided on Switchbrew.
2018-10-02 21:34:33 -04:00
Lioncash
d80dd2b812 service/lbl: Update service function table
Amends the lbl service table to include new names of functions that were
added to Switchbrew.
2018-10-02 21:15:59 -04:00
Lioncash
61b144bbf3 configure_input: Make analog mapping strings translatable
These strings are user-facing, so they should be specified as
translatable with tr().
2018-10-02 20:28:45 -04:00
Lioncash
1c7c798e9e configure_audio: Move combo box index setting to their own functions
Keeps the individual behaviors in their own functions, and cleanly
separate. We can also do a little better by converting the relevant IDs
within the core to a QString only once, instead of converting every
string into a std::string.
2018-10-02 20:24:30 -04:00
Lioncash
57e47bae32 configure_audio: Use QString::fromStdString() for converting audio device names
This ensures that the proper codec will always be used no matter what.
It also avoids relying on ASCII conversions.
2018-10-02 19:50:51 -04:00
Lioncash
226dc914b3 configure_audio: Add disambiguation comment for the volume percentage string
Disambiguates what the string represents to help translators more easily
understand what it is that they're translating. While we're at it, we
can move the code to its own function, so that we don't need to specify
the same string twice.
2018-10-02 19:45:12 -04:00
Lioncash
8f5e2a2b83 configure_graphics: Make functions internally linked where applicable
These aren't used outside of this translation unit, so they can be
internally linked.
2018-10-02 19:24:43 -04:00
Zach Hilman
215b65fe75 nso: Optimize loading of IPS patches
Avoid resource-heavy classes and remove quasi-duplicated code.
2018-10-02 17:03:38 -04:00
Weiyi Wang
fdb35760a7 string_util: unify UTF8<->UTF16 conversion to codecvt 2018-10-02 16:10:34 +02:00
Weiyi Wang
bfe84f06f2 string_util: remove TString conversion for windows
First of all they are foundamentally broken. As our convention is that std::string is always UTF-8, these functions assume that the multi-byte character version of TString (std::string) from windows is also in UTF-8, which is almost always wrong. We are not going to build multi-byte character build, and even if we do, this dirty work should be handled by frontend framework early.
2018-10-02 16:10:20 +02:00
Weiyi Wang
a4595bb939 string_util: remove ShiftJIS/CP1252 conversion function
We always use unicode internally. Any dirty work of conversion with other codec should be handled by frontend framework (Qt). Further more, ShiftJIS/CP1252 are not special (they are not code set used by 3ds, or any guest/host dependencies we have), so there is no reason to specifically include them
2018-10-02 16:10:01 +02:00
fearlessTobi
e4daf4bee5 Review comments - part 5 2018-10-02 16:04:10 +02:00
fearlessTobi
ac06105dfe Review comments -part 4 2018-10-02 15:30:49 +02:00
fearlessTobi
aa48468862 Review comments - part 3 2018-10-02 15:30:48 +02:00
Weiyi Wang
62f9409ba3 web_backend: protect jwt cache with a mutex 2018-10-02 15:30:48 +02:00
fearlessTobi
120d8f3bf7 Address more review comments 2018-10-02 15:30:48 +02:00
fearlessTobi
b4ace6ec6f Address a bunch of review comments 2018-10-02 15:30:48 +02:00
fearlessTobi
4d139943f2 Port web_service from Citra 2018-10-02 15:30:48 +02:00
fearlessTobi
5f30f95e94 Add submodules 2018-10-02 15:30:48 +02:00
Zach Hilman
6d441828e6 deconstructed_rom_directory: Force NSO loader to patch NSOs 2018-10-01 16:02:50 -04:00
Zach Hilman
003b44822a nso: Add framework to support patching of uncompressed NSOs 2018-10-01 16:02:50 -04:00
Zach Hilman
42fb4e82d3 patch_manager: Add PatchNSO function
While PatchExeFS operated on the entire directory, this function operates on the uncompressed NSO. Avoids copying decompression code to PatchManager.
2018-10-01 16:02:50 -04:00
Zach Hilman
4c2a94fa94 patch_manager: Use strings for patch type instead of enum 2018-10-01 16:02:50 -04:00
Zach Hilman
21b2411c44 file_sys: Implement function to apply IPS patches 2018-10-01 16:01:46 -04:00
Zach Hilman
3c9e70fefa nso: Replace NSOHeader padding bytes with build ID 2018-10-01 16:01:46 -04:00
bunnei
bd14f397ce Merge pull request #1407 from DarkLordZach/dlc
aoc_u: Implement functions and add support for DLC loading
2018-10-01 15:56:39 -04:00
bunnei
393042c09c Merge pull request #1422 from ReinUsesLisp/fixup-points
gl_rasterizer: Fixup unassigned point sizes
2018-10-01 10:34:53 -04:00
ReinUsesLisp
1835911425 gl_rasterizer: Fixup unassigned point sizes 2018-10-01 01:18:24 -03:00
bunnei
bc679c9b8c Merge pull request #1330 from raven02/tlds
TLDS: Add 1D sampler
2018-09-30 21:53:38 -04:00
bunnei
7c61f0c322 Merge pull request #1420 from MerryMage/dynarmic
externals: Update dynarmic to 4e6848d
2018-09-30 21:25:35 -04:00
bunnei
8391048a83 Merge pull request #1322 from bunnei/tex-cubemap
gl_rasterizer_cache: Implement cubemap textures.
2018-09-30 21:19:00 -04:00
bunnei
decc319634 Merge pull request #1403 from DarkLordZach/install-sysnand
qt: Install System TitleTypes to System NAND
2018-09-30 21:08:44 -04:00
Zach Hilman
f72046099a aoc_u: Extract AccumulateAOCTitleIDs to separate function 2018-09-30 21:07:22 -04:00
Zach Hilman
7d86a008e2 aoc_u: Implement GetAddOnContentBaseId
Command #5
2018-09-30 21:01:35 -04:00
Zach Hilman
62225ae050 aoc_u: Implement Count, List and Prepare AddOnContent
Commands #2, #3, and #7
2018-09-30 21:01:35 -04:00
Zach Hilman
aa0c82e405 romfs_factory: Read from all locations with StorageId None
Previous behavior was to assert. Seems to mirror expected game behavior.
2018-09-30 21:01:35 -04:00
Zach Hilman
32fc31fb13 patch_manager: Add DLC recognition to PatchManager 2018-09-30 21:01:35 -04:00
bunnei
8f2ad3a66d Merge pull request #1338 from raven02/service_vi
Implement ISystemDisplayService::GetDisplayMode
2018-09-30 15:39:54 -04:00
bunnei
5e2f23e2b1 Merge pull request #1417 from lioncash/context
svc: Implement svcGetThreadContext
2018-09-30 15:32:27 -04:00
bunnei
df3799a008 gl_rasterizer_cache: Fixes to how we do render to cubemap.
- Fixes issues with Splatoon 2.
2018-09-30 15:10:14 -04:00
MerryMage
3031aa9d27 externals: Update dynarmic to 4e6848d
4e6848d A32/ir_emitter: Bugfix: ExceptionRaised was producing incorrect PC
41ba9fd value: Move ImmediateToU64() to be a part of Value's interface
c6a6271 reg_alloc: Emit AVX instructions where able
aedd32a abi: Emit AVX instructions where able
f2d9337 a64_exclusive_monitor: Loosen memory ordering requirements
7ca709d travis: Make macOS builds use Xcode 10
14dd45e Fix VShift terminology
88554c4 emit_x64_vector: AVX512 implementation of EmitVectorLogicalVShiftS16
ab4e316 emit_x64_vector: AVX512 implementation of EmitVectorLogicalVShiftS64
0ea84f3 emit_x64_vector: AVX2 implementation of EmitVectorLogicalVShiftS32
c77a2c5 emit_x64_vector: AVX512 implementation of EmitVectorLogicalVShiftU16()
e9441fd emit_x64_vector: AVX2 implementation of EmitVectorLogicalVShiftU64()
0e9c33c emit_x64_vector: AVX2 implementation of EmitVectorLogicalVShiftU32()
8f85274 emit_x64_vector: SSSE3 variant of EmitVectorCountLeadingZeros8()
be05e75 Merge pull request #397 from VelocityRa/dec-shift-fix
bc328fc decoders: Cast to correctly-sized type before shifting
9c3d2d1 a64_emit_x64: Lowercase PAGE_SIZE
f538d29 emit_x64_vector_floating_point: SSE4.1 implementation of EmitFPVectorToFixed
1603a6e emit_x64_vector_floating_point: EmitFPVectorRoundInt: Use FCODE
2e1ccaf emit_x64_vector: AVX implementation for EmitVectorCountLeadingZeros8
555bfda emit_x64_vector: SSE implementation of EmitVectorCountLeadingZeros16
71c2589 externals: Update Xbyak to 5.73
1ec1b2f Squashed 'externals/xbyak/' changes from 1de435ed..42462ef9
2018-09-30 19:45:27 +01:00
bunnei
29782273ec gl_rasterizer_cache: Add check for array rendering to cubemap texture. 2018-09-30 14:31:58 -04:00
bunnei
f543b43fd0 gl_rasterizer_cache: Implement render to cubemap. 2018-09-30 14:31:58 -04:00
bunnei
15cc729ebd gl_shader_decompiler: TEXS: Implement TextureType::TextureCube. 2018-09-30 14:31:58 -04:00
bunnei
ed2e0e85c9 gl_rasterizer_cache: Add support for SurfaceTarget::TextureCubemap. 2018-09-30 14:31:58 -04:00
bunnei
871580dcd8 gl_rasterizer_cache: Implement LoadGLBuffer for Texture2DArray. 2018-09-30 14:31:57 -04:00
bunnei
a9aa1db552 gl_rasterizer_cache: Update BlitTextures to support non-Texture2D ColorTexture surfaces. 2018-09-30 14:31:57 -04:00
bunnei
2e1cdde994 gl_rasterizer_cache: Track texture target and depth in the cache. 2018-09-30 14:31:57 -04:00
bunnei
fefb003b23 gl_rasterizer_cache: Workaround for Texture2D -> Texture2DArray scenario. 2018-09-30 14:31:56 -04:00
bunnei
ce452049d3 gl_rasterizer_cache: Keep track of surface 2D size separately from total size. 2018-09-30 14:31:56 -04:00
raven02
b92b4bbeaf Fix trailing whitespace 2018-09-30 23:51:10 +08:00
Merry
f8e46d335f Merge pull request #1418 from FearlessTobi/port-4269
Port citra-emu/citra#4269: "OSX: Set MACOSX_DEPLOYMENT_TARGET to 10.13"
2018-09-30 13:51:21 +01:00
B3n30
56901912cb OSX: Set MACOSX_DEPLOYMENT_TARGET to 10.13 2018-09-30 13:34:49 +02:00
Lioncash
541c550753 kernel/svc: Implement svcGetThreadContext()
Now that we have all of the rearranging and proper structure sizes in
place, it's fairly trivial to implement svcGetThreadContext(). In the
64-bit case we can more or less just write out the context as is, minus
some minor value sanitizing. In the 32-bit case we'll need to clear out
the registers that wouldn't normally be accessible from a 32-bit
AArch32 exectuable (or process).
2018-09-30 05:29:40 -04:00
Lioncash
dccfe193a9 kernel/process: Add a data member to determine if a process is 64-bit or not.
This will be necessary for the implementation of svcGetThreadContext(),
as the kernel checks whether or not the process that owns the thread
that has it context being retrieved is a 64-bit or 32-bit process.

If the process is 32-bit, then the upper 15 general-purpose registers
and upper 16 vector registers are cleared to zero (as AArch32 only has
15 GPRs and 16 128-bit vector registers. not 31 general-purpose
registers and 32 128-bit vector registers like AArch64).
2018-09-30 05:29:40 -04:00
Lioncash
cf9d6c6f52 kernel/process: Make data member variables private
Makes the public interface consistent in terms of how accesses are done
on a process object. It also makes it slightly nicer to reason about the
logic of the process class, as we don't want to expose everything to
external code.
2018-09-30 02:30:01 -04:00
Lioncash
16145e2f21 arm_interface: Add missing fpsr/tpidr members to the ThreadContext struct
Internally within the kernel, it also includes a member variable for the
floating-point status register, and TPIDR, so we should do the same here to match
it.

While we're at it, also fix up the size of the struct and add a static
assertion to ensure it always stays the correct size.
2018-09-30 02:29:57 -04:00
raven02
4092907687 Implement ISystemDisplayService::GetDisplayMode 2018-09-30 10:04:03 +08:00
bunnei
334090fe6f Merge pull request #1414 from lioncash/ref
loader: Make the Load() function take a process as a regular reference, not a SharedPtr
2018-09-29 16:57:02 -04:00
Lioncash
a63e6f9dfd loader: Make the Load() function take a process as a regular reference, not a SharedPtr
A process should never require being reference counted in this
situation. If the handle to a process is freed before this function is
called, it's definitely a bug with our lifetime management, so we can
put the requirement in place for the API that the process must be a
valid instance.
2018-09-29 16:00:03 -04:00
bunnei
0921558a9f Merge pull request #1388 from FearlessTobi/port-4258
Port citra-emu/citra#4258: "Meta: Add gitattributes file"
2018-09-29 12:01:15 -04:00
bunnei
97c0ac3545 Merge pull request #1412 from lioncash/move
kernel/object: Remove unnecessary std::move from DynamicObjectCast()
2018-09-29 11:58:58 -04:00
bunnei
fe5962e073 Merge pull request #1411 from ReinUsesLisp/point-size
video_core: Implement point_size and add point state sync
2018-09-29 11:58:39 -04:00
bunnei
8d8366b602 Merge pull request #1406 from ReinUsesLisp/multibind-samplers
gl_state: Pack sampler bindings into a single ARB_multi_bind
2018-09-29 10:55:45 -04:00
bunnei
f7b69d61f2 Merge pull request #1395 from lioncash/vm
process/vm_manager: Initial modifications to load NPDM metadata
2018-09-29 10:54:39 -04:00
Lioncash
f4c24d0832 kernel/object: Remove unnecessary std::move from DynamicObjectCast()
boost::static_pointer_cast for boost::intrusive_ptr (what SharedPtr is),
takes its parameter by const reference. Given that, it means that this
std::move doesn't actually do anything other than obscure what the
function's actual behavior is, so we can remove this. To clarify, this
would only do something if the parameter was either taking its argument
by value, by non-const ref, or by rvalue-reference.
2018-09-28 02:17:57 -04:00
ReinUsesLisp
e3e51d3ddb video_core: Implement point_size and add point state sync 2018-09-28 02:13:29 -03:00
ReinUsesLisp
b8f1506aa5 gl_state: Pack sampler bindings into a single ARB_multi_bind 2018-09-28 02:04:22 -03:00
bunnei
f7da74d18e Merge pull request #1360 from FearlessTobi/port-3979
Port citra-emu/citra#3979 game_list: move SearchField to game_list_p.h and fix untranslated text
2018-09-27 17:09:11 -04:00
bunnei
fc2419e441 Merge pull request #1394 from lioncash/stream
stream: Preserve enum class type in GetState()
2018-09-27 17:05:03 -04:00
Mat M
29ff84ea99 Merge pull request #1389 from PhiBabin/valgrind
FPCR register was uninitialized at start up
2018-09-27 15:23:03 -04:00
bunnei
a75740b298 Merge pull request #1397 from spycrab/cmake_bin
CMake: Remove superfluous CMAKE_RUNTIME_OUTPUT_DIRECTORY assignment
2018-09-27 10:00:29 -04:00
bunnei
62048edc15 Merge pull request #1377 from FernandoS27/faster-swizzle
Improved Fast Swizzle and Legacy Swizzle
2018-09-27 10:00:04 -04:00
bunnei
8731ac87fa Merge pull request #1404 from lioncash/vfs
fsmitm_romfsbuild: Minor changes
2018-09-27 09:58:56 -04:00
Zach Hilman
70e86248fd qt: Install System TitleTypes to System NAND
Fixes an issue where installed system archive NCAs would be installed to user NAND and not recognized by games.
2018-09-27 09:20:06 -04:00
Lioncash
861580f6d2 fsmitm_romfsbuild: std::move std::vector instances in Build()
Avoids making copies of large std::vector instances where it's trivially
avoidable to do so.
2018-09-26 17:35:44 -04:00
Lioncash
7ecdaaf189 fsmitm_romfsbuild: Replace manual value aligning with Common::AlignUp()
Theres no need to do explicit bitwise arithmetic here, when we have a
function that does this with a more descriptive name.
2018-09-26 17:35:21 -04:00
bunnei
c0445006af Merge pull request #1399 from lioncash/sched
kernel/scheduler: Take ARM_Interface instances by reference
2018-09-26 16:17:18 -04:00
bunnei
efcb83fb41 Merge pull request #1400 from lioncash/header
service: Add missing headers inclusions where applicable
2018-09-26 16:11:19 -04:00
bunnei
cc866d1384 Merge pull request #1402 from ReinUsesLisp/asserts
video_core: Add asserts for CS, TFB and alpha testing
2018-09-26 16:10:55 -04:00
bunnei
92dd496fb9 Merge pull request #1401 from lioncash/vfs
vfs: Minor cleanup related changes to layered VFS code
2018-09-26 16:09:16 -04:00
bunnei
9d163c00cc Merge pull request #1398 from lioncash/macos
travis: Make macOS builds utilize Xcode 10
2018-09-26 16:08:39 -04:00
Lioncash
11104b4883 patch_manager: Invert conditionals within ApplyLayeredFS()
Avoids the need to nest code quite a bit by early-exiting in error
cases.
2018-09-25 20:09:23 -04:00
ReinUsesLisp
ab65fde9f4 video_core: Add asserts for CS, TFB and alpha testing
Add asserts for compute shader dispatching, transform feedback being
enabled and alpha testing. These have in common that they'll probably break
rendering without logging.
2018-09-25 21:07:00 -03:00
Lioncash
e3b2ef9170 vfs_vector: Amend initializer list order in VectorVfsFile's constructor initializer list
Orders the initializer list members to be in the same order that they
would be initialized in. Avoids compiler warnings.
2018-09-25 20:06:21 -04:00
Lioncash
4654f89618 fsmitm_romfsbuild: Avoid type truncation warnings
Cast where explicitly necessary and in other cases we can simply modify
the algorithm to accomodate larger data.
2018-09-25 20:06:21 -04:00
Lioncash
91b56c4928 fsmitm_romfsbuild: Remove unnecessary constructors and initializers for RomFSBuildFileContext and RomFSBuildDirectoryContext
There's no need to duplicate in-class initializers with a constructor
initializer list. std::strings also initialize to empty by default.
2018-09-25 20:06:21 -04:00
Lioncash
1f92cbc059 fsmitm_romfsbuild: Remove unnecessary loops in Build()
The std::vector instances are already initially allocated with all
entries having these values, there's no need to loop through and fill
them with it again when they aren't modified.
2018-09-25 20:06:21 -04:00
Lioncash
fa9e0f9c8b fsmitm_romfsbuild: Make auto variable into a std::size_t variable within Build()
auto x = 0;

auto-deduces x to be an int. This is undesirable when working with
unsigned values. It also causes sign conversion warnings. Instead, we
can make it a proper unsigned value with the correct width that the
following expressions operate on.
2018-09-25 20:06:21 -04:00
Lioncash
f646ca874d yuzu/main: Resolve precedence bug within CalculateRomFSEntrySize()
Ternary operators have a lower precedence than arithmetic operators, so
what was actually occurring here is "return (out + full) ? x : y" which most
definitely isn't intended, given we calculate out recursively above. We
were essentially doing a lot of work for nothing.
2018-09-25 20:06:21 -04:00
Lioncash
cbb146069a yuzu/main: Move functions stored into static std::function instances out of OnGameListDumpRomFS()
This can cause warnings about static constructors, and is also not ideal
performance-wise due to the indirection through std::function. This also
keeps the behavior itself separate from the surrounding code, which can
make it nicer to read, due to the size of the code.
2018-09-25 20:06:21 -04:00
Lioncash
57616f9758 vfs/etc: Append std:: to size_t usages
Given we just recently had a patch backport this from citra, let's try
and keep the convention uniform.
2018-09-25 20:06:21 -04:00
Lioncash
28bef31ea8 vfs_concat/vfs_layered: Remove friend declarations from ConcatenatedVfsFile
Given these are only added to the class to allow those functions to
access the private constructor, it's a better approach to just make them
static functions in the interface, to make the dependency explicit.
2018-09-25 20:06:01 -04:00
Lioncash
14e2df5610 vfs_static: Remove template byte parameter from StaticVfsFile
This converts it into a regular constructor parameter. There's no need
to make this a template parameter on the class when it functions
perfectly well as a constructor argument.

This also reduces the amount of code bloat produced by the compiler, as
it doesn't need to generate the same code for multiple different
instantiations of the same class type, but with a different fill value.
2018-09-25 17:40:53 -04:00
Lioncash
e42bb5e003 service: Add missing headers inclusions where applicable
Gets rid of a few indirect inclusions.
2018-09-25 17:14:38 -04:00
bunnei
7b81e1e525 Merge pull request #1365 from DarkLordZach/lfs
file_sys: Add support for LayeredFS mods
2018-09-25 16:59:44 -04:00
Lioncash
598e4d2f6c core_cpu: Make arm_interface instances a std::unique_ptr
This is only exposed by reference, so we can just make it a unique
pointer to get rid of the need to also use reference counting for the
pointer.
2018-09-25 16:04:56 -04:00
Lioncash
a58eefa7e4 kernel/scheduler: Take ARM_Interface instance by reference in the constructor
It doesn't make sense to allow a scheduler to be constructed around a
null pointer.
2018-09-25 16:00:17 -04:00
Lioncash
5873b14efd travis: Make macOS builds utilize Xcode 10
Keeps the used toolchain up to date and finally allows the use of
<optional> and <variant> standard library headers within the codebase.
2018-09-25 14:46:46 -04:00
spycrab
1008be9b67 CMake: Remove superfluous CMAKE_RUNTIME_OUTPUT_DIRECTORY assignment 2018-09-25 17:55:08 +02:00
bunnei
b67c1fdf38 Merge pull request #1393 from tech4me/svc
svc: Updated svc names
2018-09-25 10:47:12 -04:00
Lioncash
83377113bf memory: Dehardcode the use of fixed memory range constants
The locations of these can actually vary depending on the address space
layout, so we shouldn't be using these when determining where to map
memory or be using them as offsets for calculations. This keeps all the
memory ranges flexible and malleable based off of the virtual memory
manager instance state.
2018-09-24 22:16:03 -04:00
Lioncash
6c6f95d071 svc: Report correct memory-related values within some of the cases in svcGetInfo()
Previously, these were reporting hardcoded values, but given the regions
can change depending on the requested address spaces, these need to
report the values that the memory manager contains.
2018-09-24 22:16:03 -04:00
Lioncash
7fd598636e memory: Dehardcode the use of a 36-bit address space
Given games can also request a 32-bit or 39-bit address space, we
shouldn't be hardcoding the address space range as 36-bit.
2018-09-24 22:15:53 -04:00
Lioncash
75603b005b process/vm_manager: Amend API to allow reading parameters from NPDM metadata
Rather than hard-code the address range to be 36-bit, we can derive the
parameters from supplied NPDM metadata if the supplied exectuable
supports it. This is the bare minimum necessary for this to be possible.

The following commits will rework the memory code further to adjust to
this.
2018-09-24 17:24:50 -04:00
David
367c52ff0d Implemented fatal:u properly (#1347)
* Implemented fatal:u properly

fatal:u now is properly implemented with all the ipc cmds. Error reports/Crash reports are also now implemented for fatal:u. Crash reports save to yuzu/logs/crash_reports/
The register dump is currently known as sysmodules send all zeros. If there are any non zero values for the "registers" or the unknown values, let me know!

* Fatal:U fixups

* Made fatal:u execution break more clear

* Fatal fixups
2018-09-23 22:34:11 -04:00
David
2513e086ab Stubbed IRS (#1349)
* Stubbed IRS

Currently we have no ideal way of implementing IRS. For the time being we should have the functions stubbed until we come up with a way to emulate IRS properly.

* Added IRS to logging backend

* Forward declared shared memory for irs
2018-09-23 22:33:29 -04:00
bunnei
f2c1fd08f9 Merge pull request #1354 from ogniK5377/ssl-version
Corrected SSL::SetInterfaceVersion
2018-09-23 22:32:14 -04:00
Lioncash
2f6a611311 stream: Preserve enum class type in GetState()
Preserves the meaning/type-safetiness of the stream state instead of
making it an opaque u32. This makes it usable for other things outside
of the service HLE context.
2018-09-23 20:03:38 -04:00
tech4me
d42424ace0 svc: Updated svc names 2018-09-23 17:03:38 -07:00
David
9f3fc067bf Added glObjectLabels for renderdoc for textures and shader programs (#1384)
* Added glObjectLabels for renderdoc for textures and shader programs

* Changed hardcoded "Texture" name to reflect the texture type instead

* Removed string initialize
2018-09-23 17:55:41 -04:00
bunnei
6b05f71b67 Merge pull request #1387 from FearlessTobi/port-4245
Port citra-emu/citra#4245: "common/thread: remove YieldCPU()"
2018-09-23 17:51:53 -04:00
bunnei
5d9f001bb5 Merge pull request #1385 from FearlessTobi/port-4214
Port citra-emu/citra#4214: "Set citra-qt project as default StartUp Project in Visual Studio"
2018-09-23 17:49:27 -04:00
bunnei
d373a65d26 Merge pull request #1391 from ogniK5377/GetAudioRendererState
Added audren:u#GetAudioRendererState
2018-09-23 16:25:12 -04:00
bunnei
c6f5c7d291 Merge pull request #1392 from greggameplayer/correct-BC6H
Correction of the ComponentType of BC6H_UF16 & BC6H_SF16
2018-09-23 16:23:56 -04:00
greggameplayer
b91e2d55f3 correct BC6H 2018-09-23 19:17:22 +02:00
David Marcec
c461188f51 Added audren:u#GetAudioRendererState 2018-09-23 22:32:01 +10:00
Philippe Babin
fb6bc2c495 FPCR register was uninitialized at start up 2018-09-22 21:17:43 -04:00
bunnei
93fea4e179 Merge pull request #1378 from lioncash/thread
process: Make a few member variables private where applicable
2018-09-22 19:46:53 -04:00
James Rowe
05db48b3f8 Meta: Add gitattributes file
Github Linguist will read this file when calculating language stats for
the repository. We can use this to exclude any vendored dependencies in
externals and dist. Also makes all h files be considered cpp
2018-09-22 23:31:44 +02:00
Weiyi Wang
e0ce07aa7d common/thread: remove YieldCPU()
simply use the standard library yield()
2018-09-22 23:27:42 +02:00
James Rowe
5659d5d258 Merge pull request #1386 from jroweboy/oops
Build: Reintroduce Appveyor deploy
2018-09-22 13:15:20 -06:00
James Rowe
b23b30f9ef Build: Reintroduce Appveyor deploy
This was erroneously removed when we added travis mingw builds. We still
want to push MSVC builds to github for the time being
2018-09-22 13:09:32 -06:00
fearlessTobi
f90b75cc4c Port citra-emu/citra#4214: "Set citra-qt project as default StartUp Project in Visual Studio" 2018-09-22 12:24:02 +02:00
bunnei
fb9f273e90 Merge pull request #1380 from lioncash/const
shader_bytecode: Make operator== and operator!= of IpaMode const qualified
2018-09-22 01:37:36 -04:00
bunnei
73eea61614 Merge pull request #1382 from lioncash/inc
gl_state: Remove unused type alias
2018-09-22 01:36:21 -04:00
bunnei
0246db9ea2 Merge pull request #1376 from Subv/timestretch_trace
Logging: Change the TimeStretch::Process log from debug to trace level.
2018-09-22 01:30:28 -04:00
bunnei
2d5f51bb9b Merge pull request #1381 from valentinvanelslande/patch-1
yuzu_cmd: fix sd card directory set to nand directory
2018-09-22 01:29:28 -04:00
James Rowe
37bb2c45ee Merge pull request #1383 from DarkLordZach/game-list-interpolation
game_list: Add Qt SmoothTransformation to picture scaling
2018-09-21 20:55:49 -06:00
Zach Hilman
4f183123f5 game_list: Add Qt SmoothTransformation to picture scaling 2018-09-21 21:34:46 -04:00
Lioncash
90746c33c7 gl_state: Remove unused type alias
This isn't used anywhere within the header, so we can remove it, along
with the include that was previously necessary. This also uncovers an
indirect include in the cpp file for the assertion macros.
2018-09-21 18:22:43 -04:00
Valentin Vanelslande
19d9b0778a Update config.cpp 2018-09-21 16:16:51 -05:00
Lioncash
a8f5fd787f shader_bytecode: Lay out the Ipa-related enums better
This is more consistent with the surrounding enums.
2018-09-21 16:17:31 -04:00
Lioncash
272517cf7e shader_bytecode: Make operator== and operator!= of IpaMode const qualified
These don't affect the state of the struct and can be const member
functions.
2018-09-21 16:17:27 -04:00
zhupengfei
44228ee3ed game_list: move SearchField to game_list_p.h and fix untranslated text
I have tested and made sure the text is translatable, but this would require a translation update to take effect.
2018-09-21 18:47:41 +02:00
FernandoS27
57b44200a2 Reverse stride align restriction on FastSwizzle due to lost performance 2018-09-21 12:09:59 -04:00
FernandoS27
d2dd1289bd Join both Swizzle methods within one interface function 2018-09-21 11:42:34 -04:00
FernandoS27
41c6c4593a Standarized Legacy Swizzle to look alike FastSwizzle and use a Swizzling Table instead 2018-09-21 11:34:54 -04:00
FernandoS27
f020319a45 Remove same output bpp restriction on FastSwizzle 2018-09-21 11:10:44 -04:00
FernandoS27
68aaa83836 Improved Legacy Swizzler to be better documented and work better 2018-09-21 10:57:12 -04:00
Lioncash
48b2eda492 svc: Move most process termination code to its own function within Process
Reduces the use of Process class members externally and keeps most code
related to tearing down a process with the rest of the process code.
2018-09-21 06:07:41 -04:00
Lioncash
acfc801d14 thread/process: Move TLS slot marking/freeing to the process class
Allows making several members of the process class private, it also
avoids going through Core::CurrentProcess() just to retrieve the owning
process.
2018-09-21 03:50:12 -04:00
Subv
8ba21e28cf Logging: Change the TimeStretch::Process log from debug to trace level.
This function is called too many times and makes the debug logging basically unusable due to the spam.
2018-09-20 22:33:54 -05:00
FernandoS27
bf2f2a715f Improved fast swizzle and removed restrictions to it 2018-09-20 23:06:53 -04:00
raven02
c8f9bbbf85 Merge branch 'master' into tlds 2018-09-19 19:53:11 +08:00
David Marcec
d06f4cfc63 Corrected SSL::SetInterfaceVersion
Should be a single u32
2018-09-19 16:46:11 +10:00
raven02
b91f7d5d67 Add 1D sampler for TLDS - TexelFetch (Mario Rabbids) 2018-09-17 23:25:18 +08:00
224 changed files with 26981 additions and 1892 deletions

4
.gitattributes vendored Normal file
View File

@@ -0,0 +1,4 @@
dist/languages/* linguist-vendored
dist/qt_themes/* linguist-vendored
externals/* linguist-vendored
*.h linguist-language=cpp

6
.gitmodules vendored
View File

@@ -34,3 +34,9 @@
[submodule "soundtouch"]
path = externals/soundtouch
url = https://github.com/citra-emu/ext-soundtouch.git
[submodule "libressl"]
path = externals/libressl
url = https://github.com/citra-emu/ext-libressl-portable.git
[submodule "discord-rpc"]
path = externals/discord-rpc
url = https://github.com/discordapp/discord-rpc.git

View File

@@ -24,7 +24,7 @@ matrix:
- os: osx
env: NAME="macos build"
sudo: false
osx_image: xcode9.3
osx_image: xcode10
install: "./.travis/macos/deps.sh"
script: "./.travis/macos/build.sh"
after_success: "./.travis/macos/upload.sh"

View File

@@ -10,3 +10,7 @@ TRAVIS_JOB_ID
TRAVIS_JOB_NUMBER
TRAVIS_REPO_SLUG
TRAVIS_TAG
# yuzu specific flags
ENABLE_COMPATIBILITY_REPORTING
USE_DISCORD_PRESENCE

View File

@@ -1,4 +1,4 @@
#!/bin/bash -ex
mkdir -p "$HOME/.ccache"
docker run --env-file .travis/common/travis-ci.env -v $(pwd):/yuzu -v "$HOME/.ccache":/root/.ccache ubuntu:18.04 /bin/bash /yuzu/.travis/linux/docker.sh
docker run -e ENABLE_COMPATIBILITY_REPORTING --env-file .travis/common/travis-ci.env -v $(pwd):/yuzu -v "$HOME/.ccache":/root/.ccache ubuntu:18.04 /bin/bash /yuzu/.travis/linux/docker.sh

View File

@@ -6,7 +6,7 @@ apt-get install --no-install-recommends -y build-essential git libqt5opengl5-dev
cd /yuzu
mkdir build && cd build
cmake .. -DYUZU_USE_BUNDLED_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -G Ninja
cmake .. -DYUZU_USE_BUNDLED_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -G Ninja
ninja
ccache -s

View File

@@ -2,14 +2,14 @@
set -o pipefail
export MACOSX_DEPLOYMENT_TARGET=10.12
export MACOSX_DEPLOYMENT_TARGET=10.13
export Qt5_DIR=$(brew --prefix)/opt/qt5
export UNICORNDIR=$(pwd)/externals/unicorn
export PATH="/usr/local/opt/ccache/libexec:$PATH"
mkdir build && cd build
cmake --version
cmake .. -DYUZU_USE_BUNDLED_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON
cmake .. -DYUZU_USE_BUNDLED_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DUSE_DISCORD_PRESENCE=ON
make -j4
ccache -s

View File

@@ -15,10 +15,14 @@ CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_SDL2 "Download bundled SDL2 binaries" ON
option(ENABLE_QT "Enable the Qt frontend" ON)
CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" ON "ENABLE_QT;MSVC" OFF)
option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
option(YUZU_USE_BUNDLED_UNICORN "Build/Download bundled Unicorn" ON)
option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF)
if(NOT EXISTS ${CMAKE_SOURCE_DIR}/.git/hooks/pre-commit)
message(STATUS "Copying pre-commit hook")
file(COPY hooks/pre-commit
@@ -123,8 +127,6 @@ else()
# Avoid windows.h from including some usually unused libs like winsocks.h, since this might cause some redefinition errors.
add_definitions(/DWIN32_LEAN_AND_MEAN)
# set up output paths for executable binaries (.exe-files, and .dll-files on DLL-capable platforms)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_CONFIGURATION_TYPES Debug Release CACHE STRING "" FORCE)
# Tweak optimization settings
@@ -346,14 +348,6 @@ ELSEIF (CMAKE_SYSTEM_NAME MATCHES "^(Linux|kFreeBSD|GNU|SunOS)$")
set(PLATFORM_LIBRARIES rt)
ENDIF (APPLE)
# MINGW: GCC does not support codecvt, so use iconv instead
if (UNIX OR MINGW)
find_library(ICONV_LIBRARY NAMES iconv)
if (ICONV_LIBRARY)
list(APPEND PLATFORM_LIBRARIES ${ICONV_LIBRARY})
endif()
endif()
# Setup a custom clang-format target (if clang-format can be found) that will run
# against all the src files. This should be used before making a pull request.
# =======================================================================
@@ -440,8 +434,12 @@ enable_testing()
add_subdirectory(externals)
add_subdirectory(src)
# Set yuzu project as default StartUp Project in Visual Studio
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT yuzu)
# Set yuzu project or yuzu-cmd project as default StartUp Project in Visual Studio depending on whether QT is enabled or not
if(ENABLE_QT)
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT yuzu)
else()
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT yuzu-cmd)
endif()
# Installation instructions

View File

@@ -22,7 +22,7 @@ If clang format is found, then cmake will add a custom build target that can be
* Don't ever introduce new external dependencies into Core
* Don't use any platform specific code in Core
* Use namespaces often
* Avoid the use of C-style casts and instead prefer C++-style `static_cast` and `reinterpret_cast`. Try to avoid using `dynamic_cast`. Never use `const_cast`. The only exception to this rule is for casting between two numeric types, where C-style casts are encouraged for brevity and readability.
* Avoid the use of C-style casts and instead prefer C++-style `static_cast` and `reinterpret_cast`. Try to avoid using `dynamic_cast`. Never use `const_cast`.
### Naming Rules
* Functions: `PascalCase`

View File

@@ -39,11 +39,12 @@ before_build:
- mkdir %BUILD_TYPE%_build
- cd %BUILD_TYPE%_build
- ps: |
$COMPAT = if ($env:ENABLE_COMPATIBILITY_REPORTING -eq $null) {0} else {$env:ENABLE_COMPATIBILITY_REPORTING}
if ($env:BUILD_TYPE -eq 'msvc') {
# redirect stderr and change the exit code to prevent powershell from cancelling the build if cmake prints a warning
cmd /C 'cmake -G "Visual Studio 15 2017 Win64" -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_BUNDLED_UNICORN=1 -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON .. 2>&1 && exit 0'
cmd /C 'cmake -G "Visual Studio 15 2017 Win64" -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_BUNDLED_UNICORN=1 -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON .. 2>&1 && exit 0'
} else {
C:\msys64\usr\bin\bash.exe -lc "cmake -G 'MSYS Makefiles' -DYUZU_BUILD_UNICORN=1 -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON .. 2>&1"
C:\msys64\usr\bin\bash.exe -lc "cmake -G 'MSYS Makefiles' -DYUZU_BUILD_UNICORN=1 -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON .. 2>&1"
}
- cd ..
@@ -163,3 +164,13 @@ artifacts:
name: build
type: zip
deploy:
provider: GitHub
release: $(appveyor_repo_tag_name)
auth_token:
secure: QqePPnXbkzmXct5c8hZ2X5AbsthbI6cS1Sr+VBzcD8oUOIjfWJJKXVAQGUbQAbb0
artifact: update,build
draft: false
prerelease: false
on:
appveyor_repo_tag: true

View File

@@ -70,3 +70,28 @@ if(ENABLE_CUBEB)
set(BUILD_TESTS OFF CACHE BOOL "")
add_subdirectory(cubeb EXCLUDE_FROM_ALL)
endif()
# DiscordRPC
if (USE_DISCORD_PRESENCE)
add_subdirectory(discord-rpc EXCLUDE_FROM_ALL)
target_include_directories(discord-rpc INTERFACE ./discord-rpc/include)
endif()
if (ENABLE_WEB_SERVICE)
# LibreSSL
set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")
add_subdirectory(libressl EXCLUDE_FROM_ALL)
target_include_directories(ssl INTERFACE ./libressl/include)
target_compile_definitions(ssl PRIVATE -DHAVE_INET_NTOP)
# lurlparser
add_subdirectory(lurlparser EXCLUDE_FROM_ALL)
# httplib
add_library(httplib INTERFACE)
target_include_directories(httplib INTERFACE ./httplib)
# JSON
add_library(json-headers INTERFACE)
target_include_directories(json-headers INTERFACE ./json)
endif()

1
externals/discord-rpc vendored Submodule

Submodule externals/discord-rpc added at e32d001809

2
externals/fmt vendored

15
externals/httplib/README.md vendored Normal file
View File

@@ -0,0 +1,15 @@
From https://github.com/yhirose/cpp-httplib/commit/d9479bc0b12e8a1e8bce2d34da4feeef488581f3
MIT License
===
cpp-httplib
A C++11 header-only HTTP library.
It's extremely easy to setup. Just include httplib.h file in your code!
Inspired by Sinatra and express.
© 2017 Yuji Hirose

2344
externals/httplib/httplib.h vendored Normal file

File diff suppressed because it is too large Load Diff

9
externals/json/README.md vendored Normal file
View File

@@ -0,0 +1,9 @@
JSON for Modern C++
===================
v3.1.2
This is a mirror providing the single required header file.
The original repository can be found at:
https://github.com/nlohmann/json/commit/d2dd27dc3b8472dbaa7d66f83619b3ebcd9185fe

17300
externals/json/json.hpp vendored Normal file

File diff suppressed because it is too large Load Diff

1
externals/libressl vendored Submodule

Submodule externals/libressl added at 7d01cb01cb

8
externals/lurlparser/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,8 @@
add_library(lurlparser
LUrlParser.cpp
LUrlParser.h
)
create_target_directory_groups(lurlparser)
target_include_directories(lurlparser INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})

265
externals/lurlparser/LUrlParser.cpp vendored Normal file
View File

@@ -0,0 +1,265 @@
/*
* Lightweight URL & URI parser (RFC 1738, RFC 3986)
* https://github.com/corporateshark/LUrlParser
*
* The MIT License (MIT)
*
* Copyright (C) 2015 Sergey Kosarevsky (sk@linderdaum.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "LUrlParser.h"
#include <algorithm>
#include <cstring>
#include <stdlib.h>
// check if the scheme name is valid
static bool IsSchemeValid( const std::string& SchemeName )
{
for ( auto c : SchemeName )
{
if ( !isalpha( c ) && c != '+' && c != '-' && c != '.' ) return false;
}
return true;
}
bool LUrlParser::clParseURL::GetPort( int* OutPort ) const
{
if ( !IsValid() ) { return false; }
int Port = atoi( m_Port.c_str() );
if ( Port <= 0 || Port > 65535 ) { return false; }
if ( OutPort ) { *OutPort = Port; }
return true;
}
// based on RFC 1738 and RFC 3986
LUrlParser::clParseURL LUrlParser::clParseURL::ParseURL( const std::string& URL )
{
LUrlParser::clParseURL Result;
const char* CurrentString = URL.c_str();
/*
* <scheme>:<scheme-specific-part>
* <scheme> := [a-z\+\-\.]+
* For resiliency, programs interpreting URLs should treat upper case letters as equivalent to lower case in scheme names
*/
// try to read scheme
{
const char* LocalString = strchr( CurrentString, ':' );
if ( !LocalString )
{
return clParseURL( LUrlParserError_NoUrlCharacter );
}
// save the scheme name
Result.m_Scheme = std::string( CurrentString, LocalString - CurrentString );
if ( !IsSchemeValid( Result.m_Scheme ) )
{
return clParseURL( LUrlParserError_InvalidSchemeName );
}
// scheme should be lowercase
std::transform( Result.m_Scheme.begin(), Result.m_Scheme.end(), Result.m_Scheme.begin(), ::tolower );
// skip ':'
CurrentString = LocalString+1;
}
/*
* //<user>:<password>@<host>:<port>/<url-path>
* any ":", "@" and "/" must be normalized
*/
// skip "//"
if ( *CurrentString++ != '/' ) return clParseURL( LUrlParserError_NoDoubleSlash );
if ( *CurrentString++ != '/' ) return clParseURL( LUrlParserError_NoDoubleSlash );
// check if the user name and password are specified
bool bHasUserName = false;
const char* LocalString = CurrentString;
while ( *LocalString )
{
if ( *LocalString == '@' )
{
// user name and password are specified
bHasUserName = true;
break;
}
else if ( *LocalString == '/' )
{
// end of <host>:<port> specification
bHasUserName = false;
break;
}
LocalString++;
}
// user name and password
LocalString = CurrentString;
if ( bHasUserName )
{
// read user name
while ( *LocalString && *LocalString != ':' && *LocalString != '@' ) LocalString++;
Result.m_UserName = std::string( CurrentString, LocalString - CurrentString );
// proceed with the current pointer
CurrentString = LocalString;
if ( *CurrentString == ':' )
{
// skip ':'
CurrentString++;
// read password
LocalString = CurrentString;
while ( *LocalString && *LocalString != '@' ) LocalString++;
Result.m_Password = std::string( CurrentString, LocalString - CurrentString );
CurrentString = LocalString;
}
// skip '@'
if ( *CurrentString != '@' )
{
return clParseURL( LUrlParserError_NoAtSign );
}
CurrentString++;
}
bool bHasBracket = ( *CurrentString == '[' );
// go ahead, read the host name
LocalString = CurrentString;
while ( *LocalString )
{
if ( bHasBracket && *LocalString == ']' )
{
// end of IPv6 address
LocalString++;
break;
}
else if ( !bHasBracket && ( *LocalString == ':' || *LocalString == '/' ) )
{
// port number is specified
break;
}
LocalString++;
}
Result.m_Host = std::string( CurrentString, LocalString - CurrentString );
CurrentString = LocalString;
// is port number specified?
if ( *CurrentString == ':' )
{
CurrentString++;
// read port number
LocalString = CurrentString;
while ( *LocalString && *LocalString != '/' ) LocalString++;
Result.m_Port = std::string( CurrentString, LocalString - CurrentString );
CurrentString = LocalString;
}
// end of string
if ( !*CurrentString )
{
Result.m_ErrorCode = LUrlParserError_Ok;
return Result;
}
// skip '/'
if ( *CurrentString != '/' )
{
return clParseURL( LUrlParserError_NoSlash );
}
CurrentString++;
// parse the path
LocalString = CurrentString;
while ( *LocalString && *LocalString != '#' && *LocalString != '?' ) LocalString++;
Result.m_Path = std::string( CurrentString, LocalString - CurrentString );
CurrentString = LocalString;
// check for query
if ( *CurrentString == '?' )
{
// skip '?'
CurrentString++;
// read query
LocalString = CurrentString;
while ( *LocalString && *LocalString != '#' ) LocalString++;
Result.m_Query = std::string( CurrentString, LocalString - CurrentString );
CurrentString = LocalString;
}
// check for fragment
if ( *CurrentString == '#' )
{
// skip '#'
CurrentString++;
// read fragment
LocalString = CurrentString;
while ( *LocalString ) LocalString++;
Result.m_Fragment = std::string( CurrentString, LocalString - CurrentString );
CurrentString = LocalString;
}
Result.m_ErrorCode = LUrlParserError_Ok;
return Result;
}

78
externals/lurlparser/LUrlParser.h vendored Normal file
View File

@@ -0,0 +1,78 @@
/*
* Lightweight URL & URI parser (RFC 1738, RFC 3986)
* https://github.com/corporateshark/LUrlParser
*
* The MIT License (MIT)
*
* Copyright (C) 2015 Sergey Kosarevsky (sk@linderdaum.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include <string>
namespace LUrlParser
{
enum LUrlParserError
{
LUrlParserError_Ok = 0,
LUrlParserError_Uninitialized = 1,
LUrlParserError_NoUrlCharacter = 2,
LUrlParserError_InvalidSchemeName = 3,
LUrlParserError_NoDoubleSlash = 4,
LUrlParserError_NoAtSign = 5,
LUrlParserError_UnexpectedEndOfLine = 6,
LUrlParserError_NoSlash = 7,
};
class clParseURL
{
public:
LUrlParserError m_ErrorCode;
std::string m_Scheme;
std::string m_Host;
std::string m_Port;
std::string m_Path;
std::string m_Query;
std::string m_Fragment;
std::string m_UserName;
std::string m_Password;
clParseURL()
: m_ErrorCode( LUrlParserError_Uninitialized )
{}
/// return 'true' if the parsing was successful
bool IsValid() const { return m_ErrorCode == LUrlParserError_Ok; }
/// helper to convert the port number to int, return 'true' if the port is valid (within the 0..65535 range)
bool GetPort( int* OutPort ) const;
/// parse the URL
static clParseURL ParseURL( const std::string& URL );
private:
explicit clParseURL( LUrlParserError ErrorCode )
: m_ErrorCode( ErrorCode )
{}
};
} // namespace LUrlParser

19
externals/lurlparser/README.md vendored Normal file
View File

@@ -0,0 +1,19 @@
From https://github.com/corporateshark/LUrlParser/commit/455d5e2d27e3946f11ad0328fee9ee2628e6a8e2
MIT License
===
Lightweight URL & URI parser (RFC 1738, RFC 3986)
(C) Sergey Kosarevsky, 2015
@corporateshark sk@linderdaum.com
http://www.linderdaum.com
http://blog.linderdaum.com
=============================
A tiny and lightweight URL & URI parser (RFC 1738, RFC 3986) written in C++.

0
git Normal file
View File

View File

@@ -13,3 +13,6 @@ endif()
if (ENABLE_QT)
add_subdirectory(yuzu)
endif()
if (ENABLE_WEB_SERVICE)
add_subdirectory(web_service)
endif()

View File

@@ -30,7 +30,7 @@ public:
return info;
}
VoiceInfo& Info() {
VoiceInfo& GetInfo() {
return info;
}
@@ -51,9 +51,30 @@ private:
VoiceInfo info{};
};
class AudioRenderer::EffectState {
public:
const EffectOutStatus& GetOutStatus() const {
return out_status;
}
const EffectInStatus& GetInfo() const {
return info;
}
EffectInStatus& GetInfo() {
return info;
}
void UpdateState();
private:
EffectOutStatus out_status{};
EffectInStatus info{};
};
AudioRenderer::AudioRenderer(AudioRendererParameter params,
Kernel::SharedPtr<Kernel::Event> buffer_event)
: worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count) {
: worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count),
effects(params.effect_count) {
audio_out = std::make_unique<AudioCore::AudioOut>();
stream = audio_out->OpenStream(STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, "AudioRenderer",
@@ -79,6 +100,10 @@ u32 AudioRenderer::GetMixBufferCount() const {
return worker_params.mix_buffer_count;
}
Stream::State AudioRenderer::GetStreamState() const {
return stream->GetState();
}
std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) {
// Copy UpdateDataHeader struct
UpdateDataHeader config{};
@@ -92,11 +117,29 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_
memory_pool_count * sizeof(MemoryPoolInfo));
// Copy VoiceInfo structs
std::size_t offset{sizeof(UpdateDataHeader) + config.behavior_size + config.memory_pools_size +
config.voice_resource_size};
std::size_t voice_offset{sizeof(UpdateDataHeader) + config.behavior_size +
config.memory_pools_size + config.voice_resource_size};
for (auto& voice : voices) {
std::memcpy(&voice.Info(), input_params.data() + offset, sizeof(VoiceInfo));
offset += sizeof(VoiceInfo);
std::memcpy(&voice.GetInfo(), input_params.data() + voice_offset, sizeof(VoiceInfo));
voice_offset += sizeof(VoiceInfo);
}
std::size_t effect_offset{sizeof(UpdateDataHeader) + config.behavior_size +
config.memory_pools_size + config.voice_resource_size +
config.voices_size};
for (auto& effect : effects) {
std::memcpy(&effect.GetInfo(), input_params.data() + effect_offset, sizeof(EffectInStatus));
effect_offset += sizeof(EffectInStatus);
}
// Update memory pool state
std::vector<MemoryPoolEntry> memory_pool(memory_pool_count);
for (std::size_t index = 0; index < memory_pool.size(); ++index) {
if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestAttach) {
memory_pool[index].state = MemoryPoolStates::Attached;
} else if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestDetach) {
memory_pool[index].state = MemoryPoolStates::Detached;
}
}
// Update voices
@@ -110,14 +153,8 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_
}
}
// Update memory pool state
std::vector<MemoryPoolEntry> memory_pool(memory_pool_count);
for (std::size_t index = 0; index < memory_pool.size(); ++index) {
if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestAttach) {
memory_pool[index].state = MemoryPoolStates::Attached;
} else if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestDetach) {
memory_pool[index].state = MemoryPoolStates::Detached;
}
for (auto& effect : effects) {
effect.UpdateState();
}
// Release previous buffers and queue next ones for playback
@@ -140,6 +177,14 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_
voice_out_status_offset += sizeof(VoiceOutStatus);
}
std::size_t effect_out_status_offset{
sizeof(UpdateDataHeader) + response_data.memory_pools_size + response_data.voices_size +
response_data.voice_resource_size};
for (const auto& effect : effects) {
std::memcpy(output_params.data() + effect_out_status_offset, &effect.GetOutStatus(),
sizeof(EffectOutStatus));
effect_out_status_offset += sizeof(EffectOutStatus);
}
return output_params;
}
@@ -240,11 +285,29 @@ void AudioRenderer::VoiceState::RefreshBuffer() {
break;
}
samples = Interpolate(interp_state, std::move(samples), Info().sample_rate, STREAM_SAMPLE_RATE);
samples =
Interpolate(interp_state, std::move(samples), GetInfo().sample_rate, STREAM_SAMPLE_RATE);
is_refresh_pending = false;
}
void AudioRenderer::EffectState::UpdateState() {
if (info.is_new) {
out_status.state = EffectStatus::New;
} else {
if (info.type == Effect::Aux) {
ASSERT_MSG(Memory::Read32(info.aux_info.return_buffer_info) == 0,
"Aux buffers tried to update");
ASSERT_MSG(Memory::Read32(info.aux_info.send_buffer_info) == 0,
"Aux buffers tried to update");
ASSERT_MSG(Memory::Read32(info.aux_info.return_buffer_base) == 0,
"Aux buffers tried to update");
ASSERT_MSG(Memory::Read32(info.aux_info.send_buffer_base) == 0,
"Aux buffers tried to update");
}
}
}
static constexpr s16 ClampToS16(s32 value) {
return static_cast<s16>(std::clamp(value, -32768, 32767));
}

View File

@@ -28,6 +28,16 @@ enum class PlayState : u8 {
Paused = 2,
};
enum class Effect : u8 {
None = 0,
Aux = 2,
};
enum class EffectStatus : u8 {
None = 0,
New = 1,
};
struct AudioRendererParameter {
u32_le sample_rate;
u32_le sample_count;
@@ -128,6 +138,43 @@ struct VoiceOutStatus {
};
static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size");
struct AuxInfo {
std::array<u8, 24> input_mix_buffers;
std::array<u8, 24> output_mix_buffers;
u32_le mix_buffer_count;
u32_le sample_rate; // Stored in the aux buffer currently
u32_le sampe_count;
u64_le send_buffer_info;
u64_le send_buffer_base;
u64_le return_buffer_info;
u64_le return_buffer_base;
};
static_assert(sizeof(AuxInfo) == 0x60, "AuxInfo is an invalid size");
struct EffectInStatus {
Effect type;
u8 is_new;
u8 is_enabled;
INSERT_PADDING_BYTES(1);
u32_le mix_id;
u64_le buffer_base;
u64_le buffer_sz;
s32_le priority;
INSERT_PADDING_BYTES(4);
union {
std::array<u8, 0xa0> raw;
AuxInfo aux_info;
};
};
static_assert(sizeof(EffectInStatus) == 0xc0, "EffectInStatus is an invalid size");
struct EffectOutStatus {
EffectStatus state;
INSERT_PADDING_BYTES(0xf);
};
static_assert(sizeof(EffectOutStatus) == 0x10, "EffectOutStatus is an invalid size");
struct UpdateDataHeader {
UpdateDataHeader() {}
@@ -170,13 +217,16 @@ public:
u32 GetSampleRate() const;
u32 GetSampleCount() const;
u32 GetMixBufferCount() const;
Stream::State GetStreamState() const;
private:
class EffectState;
class VoiceState;
AudioRendererParameter worker_params;
Kernel::SharedPtr<Kernel::Event> buffer_event;
std::vector<VoiceState> voices;
std::vector<EffectState> effects;
std::unique_ptr<AudioOut> audio_out;
AudioCore::StreamPtr stream;
};

View File

@@ -49,9 +49,14 @@ void Stream::Play() {
}
void Stream::Stop() {
state = State::Stopped;
ASSERT_MSG(false, "Unimplemented");
}
Stream::State Stream::GetState() const {
return state;
}
s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const {
const std::size_t num_samples{buffer.GetSamples().size() / GetNumChannels()};
return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate);

View File

@@ -33,6 +33,12 @@ public:
Multi51Channel16,
};
/// Current state of the stream
enum class State {
Stopped,
Playing,
};
/// Callback function type, used to change guest state on a buffer being released
using ReleaseCallback = std::function<void()>;
@@ -72,13 +78,10 @@ public:
/// Gets the number of channels
u32 GetNumChannels() const;
private:
/// Current state of the stream
enum class State {
Stopped,
Playing,
};
/// Get the state
State GetState() const;
private:
/// Plays the next queued buffer in the audio stream, starting playback if necessary
void PlayNextBuffer();

View File

@@ -59,7 +59,7 @@ std::size_t TimeStretcher::Process(const s16* in, std::size_t num_in, s16* out,
m_stretch_ratio = std::max(m_stretch_ratio, 0.05);
m_sound_touch.setTempo(m_stretch_ratio);
LOG_DEBUG(Audio, "{:5}/{:5} ratio:{:0.6f} backlog:{:0.6f}", num_in, num_out, m_stretch_ratio,
LOG_TRACE(Audio, "{:5}/{:5} ratio:{:0.6f} backlog:{:0.6f}", num_in, num_out, m_stretch_ratio,
backlog_fullness);
m_sound_touch.putSamples(in, static_cast<u32>(num_in));

View File

@@ -29,7 +29,7 @@ if ($ENV{CI})
if (BUILD_VERSION)
# This leaves a trailing space on the last word, but we actually want that
# because of how it's styled in the title bar.
set(BUILD_FULLNAME "${REPO_NAME} #${BUILD_VERSION} ")
set(BUILD_FULLNAME "${REPO_NAME} ${BUILD_VERSION} ")
else()
set(BUILD_FULLNAME "")
endif()
@@ -41,6 +41,8 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in" "${CMAKE_CURRENT_SOU
add_library(common STATIC
alignment.h
assert.h
detached_tasks.cpp
detached_tasks.h
bit_field.h
bit_set.h
cityhash.cpp
@@ -87,6 +89,7 @@ add_library(common STATIC
timer.cpp
timer.h
vector_math.h
web_result.h
)
if(ARCHITECTURE_x86_64)

View File

@@ -0,0 +1,41 @@
// Copyright 2018 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <thread>
#include "common/assert.h"
#include "common/detached_tasks.h"
namespace Common {
DetachedTasks* DetachedTasks::instance = nullptr;
DetachedTasks::DetachedTasks() {
ASSERT(instance == nullptr);
instance = this;
}
void DetachedTasks::WaitForAllTasks() {
std::unique_lock<std::mutex> lock(mutex);
cv.wait(lock, [this]() { return count == 0; });
}
DetachedTasks::~DetachedTasks() {
std::unique_lock<std::mutex> lock(mutex);
ASSERT(count == 0);
instance = nullptr;
}
void DetachedTasks::AddTask(std::function<void()> task) {
std::unique_lock<std::mutex> lock(instance->mutex);
++instance->count;
std::thread([task{std::move(task)}]() {
task();
std::unique_lock<std::mutex> lock(instance->mutex);
--instance->count;
std::notify_all_at_thread_exit(instance->cv, std::move(lock));
})
.detach();
}
} // namespace Common

View File

@@ -0,0 +1,40 @@
// Copyright 2018 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <condition_variable>
#include <functional>
namespace Common {
/**
* A background manager which ensures that all detached task is finished before program exits.
*
* Some tasks, telemetry submission for example, prefer executing asynchronously and don't care
* about the result. These tasks are suitable for std::thread::detach(). However, this is unsafe if
* the task is launched just before the program exits (which is a common case for telemetry), so we
* need to block on these tasks on program exit.
*
* To make detached task safe, a single DetachedTasks object should be placed in the main(), and
* call WaitForAllTasks() after all program execution but before global/static variable destruction.
* Any potentially unsafe detached task should be executed via DetachedTasks::AddTask.
*/
class DetachedTasks {
public:
DetachedTasks();
~DetachedTasks();
void WaitForAllTasks();
static void AddTask(std::function<void()> task);
private:
static DetachedTasks* instance;
std::condition_variable cv;
std::mutex mutex;
int count = 0;
};
} // namespace Common

View File

@@ -285,7 +285,7 @@ private:
template <typename T>
void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode) {
#ifdef _MSC_VER
fstream.open(Common::UTF8ToTStr(filename).c_str(), openmode);
fstream.open(Common::UTF8ToUTF16W(filename).c_str(), openmode);
#else
fstream.open(filename.c_str(), openmode);
#endif

View File

@@ -18,6 +18,25 @@ u8 ToHexNibble(char c1) {
return 0;
}
std::vector<u8> HexStringToVector(std::string_view str, bool little_endian) {
std::vector<u8> out(str.size() / 2);
if (little_endian) {
for (std::size_t i = str.size() - 2; i <= str.size(); i -= 2)
out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
} else {
for (std::size_t i = 0; i < str.size(); i += 2)
out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
}
return out;
}
std::string HexVectorToString(const std::vector<u8>& vector, bool upper) {
std::string out;
for (u8 c : vector)
out += fmt::format(upper ? "{:02X}" : "{:02x}", c);
return out;
}
std::array<u8, 16> operator""_array16(const char* str, std::size_t len) {
if (len != 32) {
LOG_ERROR(Common,

View File

@@ -7,6 +7,7 @@
#include <array>
#include <cstddef>
#include <string>
#include <vector>
#include <fmt/format.h>
#include "common/common_types.h"
@@ -14,6 +15,8 @@ namespace Common {
u8 ToHexNibble(char c1);
std::vector<u8> HexStringToVector(std::string_view str, bool little_endian);
template <std::size_t Size, bool le = false>
std::array<u8, Size> HexStringToArray(std::string_view str) {
std::array<u8, Size> out{};
@@ -27,6 +30,8 @@ std::array<u8, Size> HexStringToArray(std::string_view str) {
return out;
}
std::string HexVectorToString(const std::vector<u8>& vector, bool upper = true);
template <std::size_t Size>
std::string HexArrayToString(std::array<u8, Size> array, bool upper = true) {
std::string out;

View File

@@ -183,6 +183,7 @@ void FileBackend::Write(const Entry& entry) {
SUB(Service, FS) \
SUB(Service, GRC) \
SUB(Service, HID) \
SUB(Service, IRS) \
SUB(Service, LBL) \
SUB(Service, LDN) \
SUB(Service, LDR) \

View File

@@ -70,6 +70,7 @@ enum class Class : ClassType {
Service_FS, ///< The FS (Filesystem) service
Service_GRC, ///< The game recording service
Service_HID, ///< The HID (Human interface device) service
Service_IRS, ///< The IRS service
Service_LBL, ///< The LBL (LCD backlight) service
Service_LDN, ///< The LDN (Local domain network) service
Service_LDR, ///< The loader service

View File

@@ -31,7 +31,7 @@ std::string FormatLogMessage(const Entry& entry) {
}
void PrintMessage(const Entry& entry) {
auto str = FormatLogMessage(entry) + '\n';
const auto str = FormatLogMessage(entry).append(1, '\n');
fputs(str.c_str(), stderr);
}

View File

@@ -20,7 +20,15 @@ constexpr char KEY_VALUE_SEPARATOR_ESCAPE[] = "$0";
constexpr char PARAM_SEPARATOR_ESCAPE[] = "$1";
constexpr char ESCAPE_CHARACTER_ESCAPE[] = "$2";
/// A placeholder for empty param packages to avoid empty strings
/// (they may be recognized as "not set" by some frontend libraries like qt)
constexpr char EMPTY_PLACEHOLDER[] = "[empty]";
ParamPackage::ParamPackage(const std::string& serialized) {
if (serialized == EMPTY_PLACEHOLDER) {
return;
}
std::vector<std::string> pairs;
Common::SplitString(serialized, PARAM_SEPARATOR, pairs);
@@ -46,7 +54,7 @@ ParamPackage::ParamPackage(std::initializer_list<DataType::value_type> list) : d
std::string ParamPackage::Serialize() const {
if (data.empty())
return "";
return EMPTY_PLACEHOLDER;
std::string result;
@@ -120,4 +128,12 @@ bool ParamPackage::Has(const std::string& key) const {
return data.find(key) != data.end();
}
void ParamPackage::Erase(const std::string& key) {
data.erase(key);
}
void ParamPackage::Clear() {
data.clear();
}
} // namespace Common

View File

@@ -32,6 +32,8 @@ public:
void Set(const std::string& key, int value);
void Set(const std::string& key, float value);
bool Has(const std::string& key) const;
void Erase(const std::string& key);
void Clear();
private:
DataType data;

View File

@@ -5,6 +5,7 @@
#include <algorithm>
#include <cctype>
#include <cerrno>
#include <codecvt>
#include <cstdio>
#include <cstdlib>
#include <cstring>
@@ -13,11 +14,7 @@
#include "common/string_util.h"
#ifdef _WIN32
#include <codecvt>
#include <windows.h>
#include "common/common_funcs.h"
#else
#include <iconv.h>
#endif
namespace Common {
@@ -195,11 +192,9 @@ std::string ReplaceAll(std::string result, const std::string& src, const std::st
return result;
}
#ifdef _WIN32
std::string UTF16ToUTF8(const std::u16string& input) {
#if _MSC_VER >= 1900
// Workaround for missing char16_t/char32_t instantiations in MSVC2015
#ifdef _MSC_VER
// Workaround for missing char16_t/char32_t instantiations in MSVC2017
std::wstring_convert<std::codecvt_utf8_utf16<__int16>, __int16> convert;
std::basic_string<__int16> tmp_buffer(input.cbegin(), input.cend());
return convert.to_bytes(tmp_buffer);
@@ -210,8 +205,8 @@ std::string UTF16ToUTF8(const std::u16string& input) {
}
std::u16string UTF8ToUTF16(const std::string& input) {
#if _MSC_VER >= 1900
// Workaround for missing char16_t/char32_t instantiations in MSVC2015
#ifdef _MSC_VER
// Workaround for missing char16_t/char32_t instantiations in MSVC2017
std::wstring_convert<std::codecvt_utf8_utf16<__int16>, __int16> convert;
auto tmp_buffer = convert.from_bytes(input);
return std::u16string(tmp_buffer.cbegin(), tmp_buffer.cend());
@@ -221,6 +216,7 @@ std::u16string UTF8ToUTF16(const std::string& input) {
#endif
}
#ifdef _WIN32
static std::wstring CPToUTF16(u32 code_page, const std::string& input) {
const auto size =
MultiByteToWideChar(code_page, 0, input.data(), static_cast<int>(input.size()), nullptr, 0);
@@ -261,124 +257,6 @@ std::wstring UTF8ToUTF16W(const std::string& input) {
return CPToUTF16(CP_UTF8, input);
}
std::string SHIFTJISToUTF8(const std::string& input) {
return UTF16ToUTF8(CPToUTF16(932, input));
}
std::string CP1252ToUTF8(const std::string& input) {
return UTF16ToUTF8(CPToUTF16(1252, input));
}
#else
template <typename T>
static std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>& input) {
iconv_t const conv_desc = iconv_open("UTF-8", fromcode);
if ((iconv_t)(-1) == conv_desc) {
LOG_ERROR(Common, "Iconv initialization failure [{}]: {}", fromcode, strerror(errno));
iconv_close(conv_desc);
return {};
}
const std::size_t in_bytes = sizeof(T) * input.size();
// Multiply by 4, which is the max number of bytes to encode a codepoint
const std::size_t out_buffer_size = 4 * in_bytes;
std::string out_buffer(out_buffer_size, '\0');
auto src_buffer = &input[0];
std::size_t src_bytes = in_bytes;
auto dst_buffer = &out_buffer[0];
std::size_t dst_bytes = out_buffer.size();
while (0 != src_bytes) {
std::size_t const iconv_result =
iconv(conv_desc, (char**)(&src_buffer), &src_bytes, &dst_buffer, &dst_bytes);
if (static_cast<std::size_t>(-1) == iconv_result) {
if (EILSEQ == errno || EINVAL == errno) {
// Try to skip the bad character
if (0 != src_bytes) {
--src_bytes;
++src_buffer;
}
} else {
LOG_ERROR(Common, "iconv failure [{}]: {}", fromcode, strerror(errno));
break;
}
}
}
std::string result;
out_buffer.resize(out_buffer_size - dst_bytes);
out_buffer.swap(result);
iconv_close(conv_desc);
return result;
}
std::u16string UTF8ToUTF16(const std::string& input) {
iconv_t const conv_desc = iconv_open("UTF-16LE", "UTF-8");
if ((iconv_t)(-1) == conv_desc) {
LOG_ERROR(Common, "Iconv initialization failure [UTF-8]: {}", strerror(errno));
iconv_close(conv_desc);
return {};
}
const std::size_t in_bytes = sizeof(char) * input.size();
// Multiply by 4, which is the max number of bytes to encode a codepoint
const std::size_t out_buffer_size = 4 * sizeof(char16_t) * in_bytes;
std::u16string out_buffer(out_buffer_size, char16_t{});
char* src_buffer = const_cast<char*>(&input[0]);
std::size_t src_bytes = in_bytes;
char* dst_buffer = (char*)(&out_buffer[0]);
std::size_t dst_bytes = out_buffer.size();
while (0 != src_bytes) {
std::size_t const iconv_result =
iconv(conv_desc, &src_buffer, &src_bytes, &dst_buffer, &dst_bytes);
if (static_cast<std::size_t>(-1) == iconv_result) {
if (EILSEQ == errno || EINVAL == errno) {
// Try to skip the bad character
if (0 != src_bytes) {
--src_bytes;
++src_buffer;
}
} else {
LOG_ERROR(Common, "iconv failure [UTF-8]: {}", strerror(errno));
break;
}
}
}
std::u16string result;
out_buffer.resize(out_buffer_size - dst_bytes);
out_buffer.swap(result);
iconv_close(conv_desc);
return result;
}
std::string UTF16ToUTF8(const std::u16string& input) {
return CodeToUTF8("UTF-16LE", input);
}
std::string CP1252ToUTF8(const std::string& input) {
// return CodeToUTF8("CP1252//TRANSLIT", input);
// return CodeToUTF8("CP1252//IGNORE", input);
return CodeToUTF8("CP1252", input);
}
std::string SHIFTJISToUTF8(const std::string& input) {
// return CodeToUTF8("CP932", input);
return CodeToUTF8("SJIS", input);
}
#endif
std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t max_len) {

View File

@@ -72,31 +72,10 @@ std::string ReplaceAll(std::string result, const std::string& src, const std::st
std::string UTF16ToUTF8(const std::u16string& input);
std::u16string UTF8ToUTF16(const std::string& input);
std::string CP1252ToUTF8(const std::string& str);
std::string SHIFTJISToUTF8(const std::string& str);
#ifdef _WIN32
std::string UTF16ToUTF8(const std::wstring& input);
std::wstring UTF8ToUTF16W(const std::string& str);
#ifdef _UNICODE
inline std::string TStrToUTF8(const std::wstring& str) {
return UTF16ToUTF8(str);
}
inline std::wstring UTF8ToTStr(const std::string& str) {
return UTF8ToUTF16W(str);
}
#else
inline std::string TStrToUTF8(const std::string& str) {
return str;
}
inline std::string UTF8ToTStr(const std::string& str) {
return str;
}
#endif
#endif
/**

View File

@@ -87,14 +87,6 @@ private:
void SleepCurrentThread(int ms);
void SwitchCurrentThread(); // On Linux, this is equal to sleep 1ms
// Use this function during a spin-wait to make the current thread
// relax while another thread is working. This may be more efficient
// than using events because event functions use kernel calls.
inline void YieldCPU() {
std::this_thread::yield();
}
void SetCurrentThreadName(const char* name);
} // namespace Common

25
src/common/web_result.h Normal file
View File

@@ -0,0 +1,25 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <string>
#include "common/common_types.h"
namespace Common {
struct WebResult {
enum class Code : u32 {
Success,
InvalidURL,
CredentialsMissing,
LibError,
HttpError,
WrongContent,
NoWebservice,
};
Code result_code;
std::string result_string;
std::string returned_data;
};
} // namespace Common

View File

@@ -34,6 +34,8 @@ add_library(core STATIC
file_sys/errors.h
file_sys/fsmitm_romfsbuild.cpp
file_sys/fsmitm_romfsbuild.h
file_sys/ips_layer.cpp
file_sys/ips_layer.h
file_sys/mode.h
file_sys/nca_metadata.cpp
file_sys/nca_metadata.h
@@ -394,6 +396,10 @@ create_target_directory_groups(core)
target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static mbedtls opus unicorn open_source_archives)
if (ENABLE_WEB_SERVICE)
target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE)
target_link_libraries(core PRIVATE web_service)
endif()
if (ARCHITECTURE_x86_64)
target_sources(core PRIVATE

View File

@@ -22,10 +22,16 @@ public:
std::array<u64, 31> cpu_registers;
u64 sp;
u64 pc;
u64 pstate;
u32 pstate;
std::array<u8, 4> padding;
std::array<u128, 32> vector_registers;
u64 fpcr;
u32 fpcr;
u32 fpsr;
u64 tpidr;
};
// Internally within the kernel, it expects the AArch64 version of the
// thread context to be 800 bytes in size.
static_assert(sizeof(ThreadContext) == 0x320);
/// Runs the CPU until an event happens
virtual void Run() = 0;

View File

@@ -86,7 +86,7 @@ public:
parent.jit->HaltExecution();
parent.SetPC(pc);
Kernel::Thread* thread = Kernel::GetCurrentThread();
parent.SaveContext(thread->context);
parent.SaveContext(thread->GetContext());
GDBStub::Break();
GDBStub::SendTrap(thread, 5);
return;
@@ -129,7 +129,8 @@ public:
};
std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const {
auto** const page_table = Core::CurrentProcess()->vm_manager.page_table.pointers.data();
auto* current_process = Core::CurrentProcess();
auto** const page_table = current_process->VMManager().page_table.pointers.data();
Dynarmic::A64::UserConfig config;
@@ -138,7 +139,7 @@ std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const {
// Memory
config.page_table = reinterpret_cast<void**>(page_table);
config.page_table_address_space_bits = Memory::ADDRESS_SPACE_BITS;
config.page_table_address_space_bits = current_process->VMManager().GetAddressSpaceWidth();
config.silently_mirror_page_table = false;
// Multi-process state
@@ -174,7 +175,7 @@ ARM_Dynarmic::ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
std::size_t core_index)
: cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), core_index{core_index},
exclusive_monitor{std::dynamic_pointer_cast<DynarmicExclusiveMonitor>(exclusive_monitor)} {
ThreadContext ctx;
ThreadContext ctx{};
inner_unicorn.SaveContext(ctx);
PageTableChanged();
LoadContext(ctx);
@@ -246,15 +247,19 @@ void ARM_Dynarmic::SaveContext(ThreadContext& ctx) {
ctx.pstate = jit->GetPstate();
ctx.vector_registers = jit->GetVectors();
ctx.fpcr = jit->GetFpcr();
ctx.fpsr = jit->GetFpsr();
ctx.tpidr = cb->tpidr_el0;
}
void ARM_Dynarmic::LoadContext(const ThreadContext& ctx) {
jit->SetRegisters(ctx.cpu_registers);
jit->SetSP(ctx.sp);
jit->SetPC(ctx.pc);
jit->SetPstate(static_cast<u32>(ctx.pstate));
jit->SetPstate(ctx.pstate);
jit->SetVectors(ctx.vector_registers);
jit->SetFpcr(static_cast<u32>(ctx.fpcr));
jit->SetFpcr(ctx.fpcr);
jit->SetFpsr(ctx.fpsr);
SetTPIDR_EL0(ctx.tpidr);
}
void ARM_Dynarmic::PrepareReschedule() {

View File

@@ -195,7 +195,7 @@ void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address);
}
Kernel::Thread* thread = Kernel::GetCurrentThread();
SaveContext(thread->context);
SaveContext(thread->GetContext());
if (last_bkpt_hit || GDBStub::GetCpuStepFlag()) {
last_bkpt_hit = false;
GDBStub::Break();

View File

@@ -64,7 +64,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
if (concat.empty())
return nullptr;
return FileSys::ConcatenateFiles(concat, dir->GetName());
return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(concat, dir->GetName());
}
return vfs->OpenFile(path, FileSys::Mode::Read);
@@ -136,7 +136,8 @@ struct System::Impl {
if (virtual_filesystem == nullptr)
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
kernel.MakeCurrentProcess(Kernel::Process::Create(kernel, "main"));
auto main_process = Kernel::Process::Create(kernel, "main");
kernel.MakeCurrentProcess(main_process.get());
cpu_barrier = std::make_shared<CpuBarrier>();
cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size());
@@ -202,7 +203,7 @@ struct System::Impl {
return init_result;
}
const Loader::ResultStatus load_result{app_loader->Load(kernel.CurrentProcess())};
const Loader::ResultStatus load_result{app_loader->Load(*kernel.CurrentProcess())};
if (load_result != Loader::ResultStatus::Success) {
LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result));
Shutdown();
@@ -361,11 +362,11 @@ const std::shared_ptr<Kernel::Scheduler>& System::Scheduler(std::size_t core_ind
return impl->cpu_cores[core_index]->Scheduler();
}
Kernel::SharedPtr<Kernel::Process>& System::CurrentProcess() {
Kernel::Process* System::CurrentProcess() {
return impl->kernel.CurrentProcess();
}
const Kernel::SharedPtr<Kernel::Process>& System::CurrentProcess() const {
const Kernel::Process* System::CurrentProcess() const {
return impl->kernel.CurrentProcess();
}

View File

@@ -174,11 +174,11 @@ public:
/// Gets the scheduler for the CPU core with the specified index
const std::shared_ptr<Kernel::Scheduler>& Scheduler(std::size_t core_index);
/// Provides a reference to the current process
Kernel::SharedPtr<Kernel::Process>& CurrentProcess();
/// Provides a pointer to the current process
Kernel::Process* CurrentProcess();
/// Provides a constant reference to the current process.
const Kernel::SharedPtr<Kernel::Process>& CurrentProcess() const;
/// Provides a constant pointer to the current process.
const Kernel::Process* CurrentProcess() const;
/// Provides a reference to the kernel instance.
Kernel::KernelCore& Kernel();
@@ -246,7 +246,7 @@ inline TelemetrySession& Telemetry() {
return System::GetInstance().TelemetrySession();
}
inline Kernel::SharedPtr<Kernel::Process>& CurrentProcess() {
inline Kernel::Process* CurrentProcess() {
return System::GetInstance().CurrentProcess();
}

View File

@@ -55,16 +55,16 @@ Cpu::Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
if (Settings::values.use_cpu_jit) {
#ifdef ARCHITECTURE_x86_64
arm_interface = std::make_shared<ARM_Dynarmic>(exclusive_monitor, core_index);
arm_interface = std::make_unique<ARM_Dynarmic>(exclusive_monitor, core_index);
#else
arm_interface = std::make_shared<ARM_Unicorn>();
arm_interface = std::make_unique<ARM_Unicorn>();
LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
#endif
} else {
arm_interface = std::make_shared<ARM_Unicorn>();
arm_interface = std::make_unique<ARM_Unicorn>();
}
scheduler = std::make_shared<Kernel::Scheduler>(arm_interface.get());
scheduler = std::make_shared<Kernel::Scheduler>(*arm_interface);
}
Cpu::~Cpu() = default;

View File

@@ -76,7 +76,7 @@ public:
private:
void Reschedule();
std::shared_ptr<ARM_Interface> arm_interface;
std::unique_ptr<ARM_Interface> arm_interface;
std::shared_ptr<CpuBarrier> cpu_barrier;
std::shared_ptr<Kernel::Scheduler> scheduler;

View File

@@ -20,7 +20,9 @@ namespace FileSys {
constexpr std::array<const char*, 0x4> partition_names = {"update", "normal", "secure", "logo"};
XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) {
XCI::XCI(VirtualFile file_)
: file(std::move(file_)), program_nca_status{Loader::ResultStatus::ErrorXCIMissingProgramNCA},
partitions(0x4) {
if (file->ReadObject(&header) != sizeof(GamecardHeader)) {
status = Loader::ResultStatus::ErrorBadXCIHeader;
return;

View File

@@ -23,8 +23,10 @@
*/
#include <cstring>
#include "common/alignment.h"
#include "common/assert.h"
#include "core/file_sys/fsmitm_romfsbuild.h"
#include "core/file_sys/ips_layer.h"
#include "core/file_sys/vfs.h"
#include "core/file_sys/vfs_vector.h"
@@ -73,7 +75,7 @@ static_assert(sizeof(RomFSFileEntry) == 0x20, "RomFSFileEntry has incorrect size
struct RomFSBuildFileContext;
struct RomFSBuildDirectoryContext {
std::string path = "";
std::string path;
u32 cur_path_ofs = 0;
u32 path_len = 0;
u32 entry_offset = 0;
@@ -84,7 +86,7 @@ struct RomFSBuildDirectoryContext {
};
struct RomFSBuildFileContext {
std::string path = "";
std::string path;
u32 cur_path_ofs = 0;
u32 path_len = 0;
u32 entry_offset = 0;
@@ -92,12 +94,10 @@ struct RomFSBuildFileContext {
u64 size = 0;
std::shared_ptr<RomFSBuildDirectoryContext> parent;
std::shared_ptr<RomFSBuildFileContext> sibling;
VirtualFile source = nullptr;
RomFSBuildFileContext() : path(""), cur_path_ofs(0), path_len(0) {}
VirtualFile source;
};
static u32 romfs_calc_path_hash(u32 parent, std::string path, u32 start, size_t path_len) {
static u32 romfs_calc_path_hash(u32 parent, std::string path, u32 start, std::size_t path_len) {
u32 hash = parent ^ 123456789;
for (u32 i = 0; i < path_len; i++) {
hash = (hash >> 5) | (hash << 27);
@@ -107,13 +107,16 @@ static u32 romfs_calc_path_hash(u32 parent, std::string path, u32 start, size_t
return hash;
}
static u32 romfs_get_hash_table_count(u32 num_entries) {
static u64 romfs_get_hash_table_count(u64 num_entries) {
if (num_entries < 3) {
return 3;
} else if (num_entries < 19) {
}
if (num_entries < 19) {
return num_entries | 1;
}
u32 count = num_entries;
u64 count = num_entries;
while (count % 2 == 0 || count % 3 == 0 || count % 5 == 0 || count % 7 == 0 ||
count % 11 == 0 || count % 13 == 0 || count % 17 == 0) {
count++;
@@ -121,7 +124,7 @@ static u32 romfs_get_hash_table_count(u32 num_entries) {
return count;
}
void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs,
void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs, VirtualDir ext,
std::shared_ptr<RomFSBuildDirectoryContext> parent) {
std::vector<std::shared_ptr<RomFSBuildDirectoryContext>> child_dirs;
@@ -139,9 +142,12 @@ void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs,
const auto child = std::make_shared<RomFSBuildDirectoryContext>();
// Set child's path.
child->cur_path_ofs = parent->path_len + 1;
child->path_len = child->cur_path_ofs + kv.first.size();
child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size());
child->path = parent->path + "/" + kv.first;
if (ext != nullptr && ext->GetFileRelative(child->path + ".stub") != nullptr)
continue;
// Sanity check on path_len
ASSERT(child->path_len < FS_MAX_PATH);
@@ -152,14 +158,27 @@ void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs,
const auto child = std::make_shared<RomFSBuildFileContext>();
// Set child's path.
child->cur_path_ofs = parent->path_len + 1;
child->path_len = child->cur_path_ofs + kv.first.size();
child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size());
child->path = parent->path + "/" + kv.first;
if (ext != nullptr && ext->GetFileRelative(child->path + ".stub") != nullptr)
continue;
// Sanity check on path_len
ASSERT(child->path_len < FS_MAX_PATH);
child->source = root_romfs->GetFileRelative(child->path);
if (ext != nullptr) {
const auto ips = ext->GetFileRelative(child->path + ".ips");
if (ips != nullptr) {
auto patched = PatchIPS(child->source, ips);
if (patched != nullptr)
child->source = std::move(patched);
}
}
child->size = child->source->GetSize();
AddFile(parent, child);
@@ -167,7 +186,7 @@ void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs,
}
for (auto& child : child_dirs) {
this->VisitDirectory(root_romfs, child);
this->VisitDirectory(root_romfs, ext, child);
}
}
@@ -181,7 +200,7 @@ bool RomFSBuildContext::AddDirectory(std::shared_ptr<RomFSBuildDirectoryContext>
// Add a new directory.
num_dirs++;
dir_table_size +=
sizeof(RomFSDirectoryEntry) + ((dir_ctx->path_len - dir_ctx->cur_path_ofs + 3) & ~3);
sizeof(RomFSDirectoryEntry) + Common::AlignUp(dir_ctx->path_len - dir_ctx->cur_path_ofs, 4);
dir_ctx->parent = parent_dir_ctx;
directories.emplace(dir_ctx->path, dir_ctx);
@@ -199,28 +218,29 @@ bool RomFSBuildContext::AddFile(std::shared_ptr<RomFSBuildDirectoryContext> pare
// Add a new file.
num_files++;
file_table_size +=
sizeof(RomFSFileEntry) + ((file_ctx->path_len - file_ctx->cur_path_ofs + 3) & ~3);
sizeof(RomFSFileEntry) + Common::AlignUp(file_ctx->path_len - file_ctx->cur_path_ofs, 4);
file_ctx->parent = parent_dir_ctx;
files.emplace(file_ctx->path, file_ctx);
return true;
}
RomFSBuildContext::RomFSBuildContext(VirtualDir base_) : base(std::move(base_)) {
RomFSBuildContext::RomFSBuildContext(VirtualDir base_, VirtualDir ext_)
: base(std::move(base_)), ext(std::move(ext_)) {
root = std::make_shared<RomFSBuildDirectoryContext>();
root->path = "\0";
directories.emplace(root->path, root);
num_dirs = 1;
dir_table_size = 0x18;
VisitDirectory(base, root);
VisitDirectory(base, ext, root);
}
RomFSBuildContext::~RomFSBuildContext() = default;
std::map<u64, VirtualFile> RomFSBuildContext::Build() {
const auto dir_hash_table_entry_count = romfs_get_hash_table_count(num_dirs);
const auto file_hash_table_entry_count = romfs_get_hash_table_count(num_files);
const u64 dir_hash_table_entry_count = romfs_get_hash_table_count(num_dirs);
const u64 file_hash_table_entry_count = romfs_get_hash_table_count(num_files);
dir_hash_table_size = 4 * dir_hash_table_entry_count;
file_hash_table_size = 4 * file_hash_table_entry_count;
@@ -233,12 +253,6 @@ std::map<u64, VirtualFile> RomFSBuildContext::Build() {
std::vector<u8> dir_table(dir_table_size);
std::vector<u8> file_table(file_table_size);
// Clear out hash tables.
for (u32 i = 0; i < dir_hash_table_entry_count; i++)
dir_hash_table[i] = ROMFS_ENTRY_EMPTY;
for (u32 i = 0; i < file_hash_table_entry_count; i++)
file_hash_table[i] = ROMFS_ENTRY_EMPTY;
std::shared_ptr<RomFSBuildFileContext> cur_file;
// Determine file offsets.
@@ -246,12 +260,12 @@ std::map<u64, VirtualFile> RomFSBuildContext::Build() {
std::shared_ptr<RomFSBuildFileContext> prev_file = nullptr;
for (const auto& it : files) {
cur_file = it.second;
file_partition_size = (file_partition_size + 0xFULL) & ~0xFULL;
file_partition_size = Common::AlignUp(file_partition_size, 16);
cur_file->offset = file_partition_size;
file_partition_size += cur_file->size;
cur_file->entry_offset = entry_offset;
entry_offset +=
sizeof(RomFSFileEntry) + ((cur_file->path_len - cur_file->cur_path_ofs + 3) & ~3);
entry_offset += sizeof(RomFSFileEntry) +
Common::AlignUp(cur_file->path_len - cur_file->cur_path_ofs, 4);
prev_file = cur_file;
}
// Assign deferred parent/sibling ownership.
@@ -268,8 +282,8 @@ std::map<u64, VirtualFile> RomFSBuildContext::Build() {
for (const auto& it : directories) {
cur_dir = it.second;
cur_dir->entry_offset = entry_offset;
entry_offset +=
sizeof(RomFSDirectoryEntry) + ((cur_dir->path_len - cur_dir->cur_path_ofs + 3) & ~3);
entry_offset += sizeof(RomFSDirectoryEntry) +
Common::AlignUp(cur_dir->path_len - cur_dir->cur_path_ofs, 4);
}
// Assign deferred parent/sibling ownership.
for (auto it = directories.rbegin(); it->second != root; ++it) {
@@ -302,7 +316,7 @@ std::map<u64, VirtualFile> RomFSBuildContext::Build() {
out.emplace(cur_file->offset + ROMFS_FILEPARTITION_OFS, cur_file->source);
std::memcpy(file_table.data() + cur_file->entry_offset, &cur_entry, sizeof(RomFSFileEntry));
std::memset(file_table.data() + cur_file->entry_offset + sizeof(RomFSFileEntry), 0,
(cur_entry.name_size + 3) & ~3);
Common::AlignUp(cur_entry.name_size, 4));
std::memcpy(file_table.data() + cur_file->entry_offset + sizeof(RomFSFileEntry),
cur_file->path.data() + cur_file->cur_path_ofs, name_size);
}
@@ -327,12 +341,10 @@ std::map<u64, VirtualFile> RomFSBuildContext::Build() {
cur_entry.name_size = name_size;
std::memcpy(dir_table.data() + cur_dir->entry_offset, &cur_entry,
sizeof(RomFSDirectoryEntry));
std::memcpy(dir_table.data() + cur_dir->entry_offset, &cur_entry,
sizeof(RomFSDirectoryEntry));
std::memset(dir_table.data() + cur_dir->entry_offset + sizeof(RomFSDirectoryEntry), 0,
(cur_entry.name_size + 3) & ~3);
Common::AlignUp(cur_entry.name_size, 4));
std::memcpy(dir_table.data() + cur_dir->entry_offset + sizeof(RomFSDirectoryEntry),
cur_dir->path.data() + cur_dir->cur_path_ofs, name_size);
}
@@ -344,18 +356,18 @@ std::map<u64, VirtualFile> RomFSBuildContext::Build() {
header.dir_hash_table_size = dir_hash_table_size;
header.dir_table_size = dir_table_size;
header.file_partition_ofs = ROMFS_FILEPARTITION_OFS;
header.dir_hash_table_ofs = (header.file_partition_ofs + file_partition_size + 3ULL) & ~3ULL;
header.dir_hash_table_ofs = Common::AlignUp(header.file_partition_ofs + file_partition_size, 4);
header.dir_table_ofs = header.dir_hash_table_ofs + header.dir_hash_table_size;
header.file_hash_table_ofs = header.dir_table_ofs + header.dir_table_size;
header.file_table_ofs = header.file_hash_table_ofs + header.file_hash_table_size;
std::vector<u8> header_data(sizeof(RomFSHeader));
std::memcpy(header_data.data(), &header, header_data.size());
out.emplace(0, std::make_shared<VectorVfsFile>(header_data));
out.emplace(0, std::make_shared<VectorVfsFile>(std::move(header_data)));
std::vector<u8> metadata(file_hash_table_size + file_table_size + dir_hash_table_size +
dir_table_size);
auto index = 0;
std::size_t index = 0;
std::memcpy(metadata.data(), dir_hash_table.data(), dir_hash_table.size() * sizeof(u32));
index += dir_hash_table.size() * sizeof(u32);
std::memcpy(metadata.data() + index, dir_table.data(), dir_table.size());
@@ -364,7 +376,7 @@ std::map<u64, VirtualFile> RomFSBuildContext::Build() {
file_hash_table.size() * sizeof(u32));
index += file_hash_table.size() * sizeof(u32);
std::memcpy(metadata.data() + index, file_table.data(), file_table.size());
out.emplace(header.dir_hash_table_ofs, std::make_shared<VectorVfsFile>(metadata));
out.emplace(header.dir_hash_table_ofs, std::make_shared<VectorVfsFile>(std::move(metadata)));
return out;
}

View File

@@ -40,7 +40,7 @@ struct RomFSFileEntry;
class RomFSBuildContext {
public:
explicit RomFSBuildContext(VirtualDir base);
explicit RomFSBuildContext(VirtualDir base, VirtualDir ext = nullptr);
~RomFSBuildContext();
// This finalizes the context.
@@ -48,6 +48,7 @@ public:
private:
VirtualDir base;
VirtualDir ext;
std::shared_ptr<RomFSBuildDirectoryContext> root;
std::map<std::string, std::shared_ptr<RomFSBuildDirectoryContext>, std::less<>> directories;
std::map<std::string, std::shared_ptr<RomFSBuildFileContext>, std::less<>> files;
@@ -59,7 +60,8 @@ private:
u64 file_hash_table_size = 0;
u64 file_partition_size = 0;
void VisitDirectory(VirtualDir filesys, std::shared_ptr<RomFSBuildDirectoryContext> parent);
void VisitDirectory(VirtualDir filesys, VirtualDir ext,
std::shared_ptr<RomFSBuildDirectoryContext> parent);
bool AddDirectory(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx,
std::shared_ptr<RomFSBuildDirectoryContext> dir_ctx);

View File

@@ -0,0 +1,338 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <cstring>
#include <map>
#include <sstream>
#include <string>
#include <utility>
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "common/swap.h"
#include "core/file_sys/ips_layer.h"
#include "core/file_sys/vfs_vector.h"
namespace FileSys {
enum class IPSFileType {
IPS,
IPS32,
Error,
};
constexpr std::array<std::pair<const char*, const char*>, 11> ESCAPE_CHARACTER_MAP{{
{"\\a", "\a"},
{"\\b", "\b"},
{"\\f", "\f"},
{"\\n", "\n"},
{"\\r", "\r"},
{"\\t", "\t"},
{"\\v", "\v"},
{"\\\\", "\\"},
{"\\\'", "\'"},
{"\\\"", "\""},
{"\\\?", "\?"},
}};
static IPSFileType IdentifyMagic(const std::vector<u8>& magic) {
if (magic.size() != 5) {
return IPSFileType::Error;
}
constexpr std::array<u8, 5> patch_magic{{'P', 'A', 'T', 'C', 'H'}};
if (std::equal(magic.begin(), magic.end(), patch_magic.begin())) {
return IPSFileType::IPS;
}
constexpr std::array<u8, 5> ips32_magic{{'I', 'P', 'S', '3', '2'}};
if (std::equal(magic.begin(), magic.end(), ips32_magic.begin())) {
return IPSFileType::IPS32;
}
return IPSFileType::Error;
}
static bool IsEOF(IPSFileType type, const std::vector<u8>& data) {
constexpr std::array<u8, 3> eof{{'E', 'O', 'F'}};
if (type == IPSFileType::IPS && std::equal(data.begin(), data.end(), eof.begin())) {
return true;
}
constexpr std::array<u8, 4> eeof{{'E', 'E', 'O', 'F'}};
return type == IPSFileType::IPS32 && std::equal(data.begin(), data.end(), eeof.begin());
}
VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
if (in == nullptr || ips == nullptr)
return nullptr;
const auto type = IdentifyMagic(ips->ReadBytes(0x5));
if (type == IPSFileType::Error)
return nullptr;
auto in_data = in->ReadAllBytes();
std::vector<u8> temp(type == IPSFileType::IPS ? 3 : 4);
u64 offset = 5; // After header
while (ips->Read(temp.data(), temp.size(), offset) == temp.size()) {
offset += temp.size();
if (IsEOF(type, temp)) {
break;
}
u32 real_offset{};
if (type == IPSFileType::IPS32)
real_offset = (temp[0] << 24) | (temp[1] << 16) | (temp[2] << 8) | temp[3];
else
real_offset = (temp[0] << 16) | (temp[1] << 8) | temp[2];
u16 data_size{};
if (ips->ReadObject(&data_size, offset) != sizeof(u16))
return nullptr;
data_size = Common::swap16(data_size);
offset += sizeof(u16);
if (data_size == 0) { // RLE
u16 rle_size{};
if (ips->ReadObject(&rle_size, offset) != sizeof(u16))
return nullptr;
rle_size = Common::swap16(data_size);
offset += sizeof(u16);
const auto data = ips->ReadByte(offset++);
if (data == boost::none)
return nullptr;
if (real_offset + rle_size > in_data.size())
rle_size = static_cast<u16>(in_data.size() - real_offset);
std::memset(in_data.data() + real_offset, data.get(), rle_size);
} else { // Standard Patch
auto read = data_size;
if (real_offset + read > in_data.size())
read = static_cast<u16>(in_data.size() - real_offset);
if (ips->Read(in_data.data() + real_offset, read, offset) != data_size)
return nullptr;
offset += data_size;
}
}
if (!IsEOF(type, temp)) {
return nullptr;
}
return std::make_shared<VectorVfsFile>(std::move(in_data), in->GetName(),
in->GetContainingDirectory());
}
struct IPSwitchCompiler::IPSwitchPatch {
std::string name;
bool enabled;
std::map<u32, std::vector<u8>> records;
};
IPSwitchCompiler::IPSwitchCompiler(VirtualFile patch_text_) : patch_text(std::move(patch_text_)) {
Parse();
}
IPSwitchCompiler::~IPSwitchCompiler() = default;
std::array<u8, 32> IPSwitchCompiler::GetBuildID() const {
return nso_build_id;
}
bool IPSwitchCompiler::IsValid() const {
return valid;
}
static bool StartsWith(std::string_view base, std::string_view check) {
return base.size() >= check.size() && base.substr(0, check.size()) == check;
}
static std::string EscapeStringSequences(std::string in) {
for (const auto& seq : ESCAPE_CHARACTER_MAP) {
for (auto index = in.find(seq.first); index != std::string::npos;
index = in.find(seq.first, index)) {
in.replace(index, std::strlen(seq.first), seq.second);
index += std::strlen(seq.second);
}
}
return in;
}
void IPSwitchCompiler::ParseFlag(const std::string& line) {
if (StartsWith(line, "@flag offset_shift ")) {
// Offset Shift Flag
offset_shift = std::stoll(line.substr(19), nullptr, 0);
} else if (StartsWith(line, "@little-endian")) {
// Set values to read as little endian
is_little_endian = true;
} else if (StartsWith(line, "@big-endian")) {
// Set values to read as big endian
is_little_endian = false;
} else if (StartsWith(line, "@flag print_values")) {
// Force printing of applied values
print_values = true;
}
}
void IPSwitchCompiler::Parse() {
const auto bytes = patch_text->ReadAllBytes();
std::stringstream s;
s.write(reinterpret_cast<const char*>(bytes.data()), bytes.size());
std::vector<std::string> lines;
std::string stream_line;
while (std::getline(s, stream_line)) {
// Remove a trailing \r
if (!stream_line.empty() && stream_line.back() == '\r')
stream_line.pop_back();
lines.push_back(std::move(stream_line));
}
for (std::size_t i = 0; i < lines.size(); ++i) {
auto line = lines[i];
// Remove midline comments
std::size_t comment_index = std::string::npos;
bool within_string = false;
for (std::size_t k = 0; k < line.size(); ++k) {
if (line[k] == '\"' && (k > 0 && line[k - 1] != '\\')) {
within_string = !within_string;
} else if (line[k] == '\\' && (k < line.size() - 1 && line[k + 1] == '\\')) {
comment_index = k;
break;
}
}
if (!StartsWith(line, "//") && comment_index != std::string::npos) {
last_comment = line.substr(comment_index + 2);
line = line.substr(0, comment_index);
}
if (StartsWith(line, "@stop")) {
// Force stop
break;
} else if (StartsWith(line, "@nsobid-")) {
// NSO Build ID Specifier
auto raw_build_id = line.substr(8);
if (raw_build_id.size() != 0x40)
raw_build_id.resize(0x40, '0');
nso_build_id = Common::HexStringToArray<0x20>(raw_build_id);
} else if (StartsWith(line, "#")) {
// Mandatory Comment
LOG_INFO(Loader, "[IPSwitchCompiler ('{}')] Forced output comment: {}",
patch_text->GetName(), line.substr(1));
} else if (StartsWith(line, "//")) {
// Normal Comment
last_comment = line.substr(2);
if (last_comment.find_first_not_of(' ') == std::string::npos)
continue;
if (last_comment.find_first_not_of(' ') != 0)
last_comment = last_comment.substr(last_comment.find_first_not_of(' '));
} else if (StartsWith(line, "@enabled") || StartsWith(line, "@disabled")) {
// Start of patch
const auto enabled = StartsWith(line, "@enabled");
if (i == 0)
return;
LOG_INFO(Loader, "[IPSwitchCompiler ('{}')] Parsing patch '{}' ({})",
patch_text->GetName(), last_comment, line.substr(1));
IPSwitchPatch patch{last_comment, enabled, {}};
// Read rest of patch
while (true) {
if (i + 1 >= lines.size())
break;
const auto patch_line = lines[++i];
// Start of new patch
if (StartsWith(patch_line, "@enabled") || StartsWith(patch_line, "@disabled")) {
--i;
break;
}
// Check for a flag
if (StartsWith(patch_line, "@")) {
ParseFlag(patch_line);
continue;
}
// 11 - 8 hex digit offset + space + minimum two digit overwrite val
if (patch_line.length() < 11)
break;
auto offset = std::stoul(patch_line.substr(0, 8), nullptr, 16);
offset += static_cast<unsigned long>(offset_shift);
std::vector<u8> replace;
// 9 - first char of replacement val
if (patch_line[9] == '\"') {
// string replacement
auto end_index = patch_line.find('\"', 10);
if (end_index == std::string::npos || end_index < 10)
return;
while (patch_line[end_index - 1] == '\\') {
end_index = patch_line.find('\"', end_index + 1);
if (end_index == std::string::npos || end_index < 10)
return;
}
auto value = patch_line.substr(10, end_index - 10);
value = EscapeStringSequences(value);
replace.reserve(value.size());
std::copy(value.begin(), value.end(), std::back_inserter(replace));
} else {
// hex replacement
const auto value = patch_line.substr(9);
replace.reserve(value.size() / 2);
replace = Common::HexStringToVector(value, is_little_endian);
}
if (print_values) {
LOG_INFO(Loader,
"[IPSwitchCompiler ('{}')] - Patching value at offset 0x{:08X} "
"with byte string '{}'",
patch_text->GetName(), offset, Common::HexVectorToString(replace));
}
patch.records.insert_or_assign(offset, std::move(replace));
}
patches.push_back(std::move(patch));
} else if (StartsWith(line, "@")) {
ParseFlag(line);
}
}
valid = true;
}
VirtualFile IPSwitchCompiler::Apply(const VirtualFile& in) const {
if (in == nullptr || !valid)
return nullptr;
auto in_data = in->ReadAllBytes();
for (const auto& patch : patches) {
if (!patch.enabled)
continue;
for (const auto& record : patch.records) {
if (record.first >= in_data.size())
continue;
auto replace_size = record.second.size();
if (record.first + replace_size > in_data.size())
replace_size = in_data.size() - record.first;
for (std::size_t i = 0; i < replace_size; ++i)
in_data[i + record.first] = record.second[i];
}
}
return std::make_shared<VectorVfsFile>(std::move(in_data), in->GetName(),
in->GetContainingDirectory());
}
} // namespace FileSys

View File

@@ -0,0 +1,44 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <memory>
#include <vector>
#include "common/common_types.h"
#include "core/file_sys/vfs.h"
namespace FileSys {
VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips);
class IPSwitchCompiler {
public:
explicit IPSwitchCompiler(VirtualFile patch_text);
~IPSwitchCompiler();
std::array<u8, 0x20> GetBuildID() const;
bool IsValid() const;
VirtualFile Apply(const VirtualFile& in) const;
private:
struct IPSwitchPatch;
void ParseFlag(const std::string& flag);
void Parse();
bool valid = false;
VirtualFile patch_text;
std::vector<IPSwitchPatch> patches;
std::array<u8, 0x20> nso_build_id{};
bool is_little_endian = false;
s64 offset_shift = 0;
bool print_values = false;
std::string last_comment = "";
};
} // namespace FileSys

View File

@@ -2,22 +2,36 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <array>
#include <cstddef>
#include <cstring>
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/ips_layer.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs.h"
#include "core/file_sys/vfs_layered.h"
#include "core/file_sys/vfs_vector.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h"
namespace FileSys {
constexpr u64 SINGLE_BYTE_MODULUS = 0x100;
constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000;
struct NSOBuildHeader {
u32_le magic;
INSERT_PADDING_BYTES(0x3C);
std::array<u8, 0x20> build_id;
INSERT_PADDING_BYTES(0xA0);
};
static_assert(sizeof(NSOBuildHeader) == 0x100, "NSOBuildHeader has incorrect size.");
std::string FormatTitleVersion(u32 version, TitleVersionFormat format) {
std::array<u8, sizeof(u32)> bytes{};
@@ -32,15 +46,6 @@ std::string FormatTitleVersion(u32 version, TitleVersionFormat format) {
return fmt::format("v{}.{}.{}", bytes[3], bytes[2], bytes[1]);
}
constexpr std::array<const char*, 2> PATCH_TYPE_NAMES{
"Update",
"LayeredFS",
};
std::string FormatPatchTypeName(PatchType type) {
return PATCH_TYPE_NAMES.at(static_cast<std::size_t>(type));
}
PatchManager::PatchManager(u64 title_id) : title_id(title_id) {}
PatchManager::~PatchManager() = default;
@@ -68,44 +73,147 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
return exefs;
}
static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) {
const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
if (type == ContentRecordType::Program && load_dir != nullptr && load_dir->GetSize() > 0) {
auto extracted = ExtractRomFS(romfs);
static std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs,
const std::string& build_id) {
std::vector<VirtualFile> out;
out.reserve(patch_dirs.size());
for (const auto& subdir : patch_dirs) {
auto exefs_dir = subdir->GetSubdirectory("exefs");
if (exefs_dir != nullptr) {
for (const auto& file : exefs_dir->GetFiles()) {
if (file->GetExtension() == "ips") {
auto name = file->GetName();
const auto p1 = name.substr(0, name.find('.'));
const auto this_build_id = p1.substr(0, p1.find_last_not_of('0') + 1);
if (extracted != nullptr) {
auto patch_dirs = load_dir->GetSubdirectories();
std::sort(patch_dirs.begin(), patch_dirs.end(),
[](const VirtualDir& l, const VirtualDir& r) {
return l->GetName() < r->GetName();
});
if (build_id == this_build_id)
out.push_back(file);
} else if (file->GetExtension() == "pchtxt") {
IPSwitchCompiler compiler{file};
if (!compiler.IsValid())
continue;
std::vector<VirtualDir> layers;
layers.reserve(patch_dirs.size() + 1);
for (const auto& subdir : patch_dirs) {
auto romfs_dir = subdir->GetSubdirectory("romfs");
if (romfs_dir != nullptr)
layers.push_back(std::move(romfs_dir));
}
auto this_build_id = Common::HexArrayToString(compiler.GetBuildID());
this_build_id =
this_build_id.substr(0, this_build_id.find_last_not_of('0') + 1);
layers.push_back(std::move(extracted));
const auto layered = LayerDirectories(layers);
if (layered != nullptr) {
auto packed = CreateRomFS(layered);
if (packed != nullptr) {
LOG_INFO(Loader, " RomFS: LayeredFS patches applied successfully");
romfs = std::move(packed);
if (build_id == this_build_id)
out.push_back(file);
}
}
}
}
return out;
}
VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset,
ContentRecordType type) const {
std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const {
if (nso.size() < 0x100)
return nso;
NSOBuildHeader header;
std::memcpy(&header, nso.data(), sizeof(NSOBuildHeader));
if (header.magic != Common::MakeMagic('N', 'S', 'O', '0'))
return nso;
const auto build_id_raw = Common::HexArrayToString(header.build_id);
const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1);
LOG_INFO(Loader, "Patching NSO for build_id={}", build_id);
const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
auto patch_dirs = load_dir->GetSubdirectories();
std::sort(patch_dirs.begin(), patch_dirs.end(),
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
const auto patches = CollectPatches(patch_dirs, build_id);
auto out = nso;
for (const auto& patch_file : patches) {
if (patch_file->GetExtension() == "ips") {
LOG_INFO(Loader, " - Applying IPS patch from mod \"{}\"",
patch_file->GetContainingDirectory()->GetParentDirectory()->GetName());
const auto patched = PatchIPS(std::make_shared<VectorVfsFile>(out), patch_file);
if (patched != nullptr)
out = patched->ReadAllBytes();
} else if (patch_file->GetExtension() == "pchtxt") {
LOG_INFO(Loader, " - Applying IPSwitch patch from mod \"{}\"",
patch_file->GetContainingDirectory()->GetParentDirectory()->GetName());
const IPSwitchCompiler compiler{patch_file};
const auto patched = compiler.Apply(std::make_shared<VectorVfsFile>(out));
if (patched != nullptr)
out = patched->ReadAllBytes();
}
}
if (out.size() < 0x100)
return nso;
std::memcpy(out.data(), &header, sizeof(NSOBuildHeader));
return out;
}
bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
const auto build_id_raw = Common::HexArrayToString(build_id_);
const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1);
LOG_INFO(Loader, "Querying NSO patch existence for build_id={}", build_id);
const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
auto patch_dirs = load_dir->GetSubdirectories();
std::sort(patch_dirs.begin(), patch_dirs.end(),
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
return !CollectPatches(patch_dirs, build_id).empty();
}
static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) {
const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
if (type != ContentRecordType::Program || load_dir == nullptr || load_dir->GetSize() <= 0) {
return;
}
auto extracted = ExtractRomFS(romfs);
if (extracted == nullptr) {
return;
}
auto patch_dirs = load_dir->GetSubdirectories();
std::sort(patch_dirs.begin(), patch_dirs.end(),
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
std::vector<VirtualDir> layers;
std::vector<VirtualDir> layers_ext;
layers.reserve(patch_dirs.size() + 1);
layers_ext.reserve(patch_dirs.size() + 1);
for (const auto& subdir : patch_dirs) {
auto romfs_dir = subdir->GetSubdirectory("romfs");
if (romfs_dir != nullptr)
layers.push_back(std::move(romfs_dir));
auto ext_dir = subdir->GetSubdirectory("romfs_ext");
if (ext_dir != nullptr)
layers_ext.push_back(std::move(ext_dir));
}
layers.push_back(std::move(extracted));
auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers));
if (layered == nullptr) {
return;
}
auto layered_ext = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers_ext));
auto packed = CreateRomFS(std::move(layered), std::move(layered_ext));
if (packed == nullptr) {
return;
}
LOG_INFO(Loader, " RomFS: LayeredFS patches applied successfully");
romfs = std::move(packed);
}
VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, ContentRecordType type,
VirtualFile update_raw) const {
LOG_INFO(Loader, "Patching RomFS for title_id={:016X}, type={:02X}", title_id,
static_cast<u8>(type));
@@ -125,6 +233,13 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset,
FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0)));
romfs = new_nca->GetRomFS();
}
} else if (update_raw != nullptr) {
const auto new_nca = std::make_shared<NCA>(update_raw, romfs, ivfc_offset);
if (new_nca->GetStatus() == Loader::ResultStatus::Success &&
new_nca->GetRomFS() != nullptr) {
LOG_INFO(Loader, " RomFS: Update (PACKED) applied successfully");
romfs = new_nca->GetRomFS();
}
}
// LayeredFS
@@ -133,52 +248,119 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset,
return romfs;
}
std::map<PatchType, std::string> PatchManager::GetPatchVersionNames() const {
std::map<PatchType, std::string> out;
static void AppendCommaIfNotEmpty(std::string& to, const std::string& with) {
if (to.empty())
to += with;
else
to += ", " + with;
}
static bool IsDirValidAndNonEmpty(const VirtualDir& dir) {
return dir != nullptr && (!dir->GetFiles().empty() || !dir->GetSubdirectories().empty());
}
std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames(
VirtualFile update_raw) const {
std::map<std::string, std::string, std::less<>> out;
const auto installed = Service::FileSystem::GetUnionContents();
// Game Updates
const auto update_tid = GetUpdateTitleID(title_id);
PatchManager update{update_tid};
auto [nacp, discard_icon_file] = update.GetControlMetadata();
if (nacp != nullptr) {
out[PatchType::Update] = nacp->GetVersionString();
out.insert_or_assign("Update", nacp->GetVersionString());
} else {
if (installed->HasEntry(update_tid, ContentRecordType::Program)) {
const auto meta_ver = installed->GetEntryVersion(update_tid);
if (meta_ver == boost::none || meta_ver.get() == 0) {
out[PatchType::Update] = "";
out.insert_or_assign("Update", "");
} else {
out[PatchType::Update] =
FormatTitleVersion(meta_ver.get(), TitleVersionFormat::ThreeElements);
out.insert_or_assign(
"Update",
FormatTitleVersion(meta_ver.get(), TitleVersionFormat::ThreeElements));
}
} else if (update_raw != nullptr) {
out.insert_or_assign("Update", "PACKED");
}
}
const auto lfs_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
if (lfs_dir != nullptr && lfs_dir->GetSize() > 0)
out.insert_or_assign(PatchType::LayeredFS, "");
// General Mods (LayeredFS and IPS)
const auto mod_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
if (mod_dir != nullptr && mod_dir->GetSize() > 0) {
for (const auto& mod : mod_dir->GetSubdirectories()) {
std::string types;
const auto exefs_dir = mod->GetSubdirectory("exefs");
if (IsDirValidAndNonEmpty(exefs_dir)) {
bool ips = false;
bool ipswitch = false;
for (const auto& file : exefs_dir->GetFiles()) {
if (file->GetExtension() == "ips")
ips = true;
else if (file->GetExtension() == "pchtxt")
ipswitch = true;
}
if (ips)
AppendCommaIfNotEmpty(types, "IPS");
if (ipswitch)
AppendCommaIfNotEmpty(types, "IPSwitch");
}
if (IsDirValidAndNonEmpty(mod->GetSubdirectory("romfs")))
AppendCommaIfNotEmpty(types, "LayeredFS");
if (types.empty())
continue;
out.insert_or_assign(mod->GetName(), types);
}
}
// DLC
const auto dlc_entries = installed->ListEntriesFilter(TitleType::AOC, ContentRecordType::Data);
std::vector<RegisteredCacheEntry> dlc_match;
dlc_match.reserve(dlc_entries.size());
std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match),
[this, &installed](const RegisteredCacheEntry& entry) {
return (entry.title_id & DLC_BASE_TITLE_ID_MASK) == title_id &&
installed->GetEntry(entry)->GetStatus() ==
Loader::ResultStatus::Success;
});
if (!dlc_match.empty()) {
// Ensure sorted so DLC IDs show in order.
std::sort(dlc_match.begin(), dlc_match.end());
std::string list;
for (size_t i = 0; i < dlc_match.size() - 1; ++i)
list += fmt::format("{}, ", dlc_match[i].title_id & 0x7FF);
list += fmt::format("{}", dlc_match.back().title_id & 0x7FF);
out.insert_or_assign("DLC", std::move(list));
}
return out;
}
std::pair<std::shared_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const {
std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const {
const auto& installed{Service::FileSystem::GetUnionContents()};
const auto base_control_nca = installed->GetEntry(title_id, ContentRecordType::Control);
if (base_control_nca == nullptr)
return {};
return ParseControlNCA(base_control_nca);
return ParseControlNCA(*base_control_nca);
}
std::pair<std::shared_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA(
const std::shared_ptr<NCA>& nca) const {
const auto base_romfs = nca->GetRomFS();
std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA(const NCA& nca) const {
const auto base_romfs = nca.GetRomFS();
if (base_romfs == nullptr)
return {};
const auto romfs = PatchRomFS(base_romfs, nca->GetBaseIVFCOffset(), ContentRecordType::Control);
const auto romfs = PatchRomFS(base_romfs, nca.GetBaseIVFCOffset(), ContentRecordType::Control);
if (romfs == nullptr)
return {};
@@ -190,7 +372,7 @@ std::pair<std::shared_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA(
if (nacp_file == nullptr)
nacp_file = extracted->GetFile("Control.nacp");
const auto nacp = nacp_file == nullptr ? nullptr : std::make_shared<NACP>(nacp_file);
auto nacp = nacp_file == nullptr ? nullptr : std::make_unique<NACP>(nacp_file);
VirtualFile icon_file;
for (const auto& language : FileSys::LANGUAGE_NAMES) {
@@ -199,6 +381,6 @@ std::pair<std::shared_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA(
break;
}
return {nacp, icon_file};
return {std::move(nacp), icon_file};
}
} // namespace FileSys

View File

@@ -24,13 +24,6 @@ enum class TitleVersionFormat : u8 {
std::string FormatTitleVersion(u32 version,
TitleVersionFormat format = TitleVersionFormat::ThreeElements);
enum class PatchType {
Update,
LayeredFS,
};
std::string FormatPatchTypeName(PatchType type);
// A centralized class to manage patches to games.
class PatchManager {
public:
@@ -41,23 +34,33 @@ public:
// - Game Updates
VirtualDir PatchExeFS(VirtualDir exefs) const;
// Currently tracked NSO patches:
// - IPS
// - IPSwitch
std::vector<u8> PatchNSO(const std::vector<u8>& nso) const;
// Checks to see if PatchNSO() will have any effect given the NSO's build ID.
// Used to prevent expensive copies in NSO loader.
bool HasNSOPatch(const std::array<u8, 0x20>& build_id) const;
// Currently tracked RomFS patches:
// - Game Updates
// - LayeredFS
VirtualFile PatchRomFS(VirtualFile base, u64 ivfc_offset,
ContentRecordType type = ContentRecordType::Program) const;
ContentRecordType type = ContentRecordType::Program,
VirtualFile update_raw = nullptr) const;
// Returns a vector of pairs between patch names and patch versions.
// i.e. Update v80 will return {Update, 80}
std::map<PatchType, std::string> GetPatchVersionNames() const;
// i.e. Update 3.2.2 will return {"Update", "3.2.2"}
std::map<std::string, std::string, std::less<>> GetPatchVersionNames(
VirtualFile update_raw = nullptr) const;
// Given title_id of the program, attempts to get the control data of the update and parse it,
// falling back to the base control data.
std::pair<std::shared_ptr<NACP>, VirtualFile> GetControlMetadata() const;
std::pair<std::unique_ptr<NACP>, VirtualFile> GetControlMetadata() const;
// Version of GetControlMetadata that takes an arbitrary NCA
std::pair<std::shared_ptr<NACP>, VirtualFile> ParseControlNCA(
const std::shared_ptr<NCA>& nca) const;
std::pair<std::unique_ptr<NACP>, VirtualFile> ParseControlNCA(const NCA& nca) const;
private:
u64 title_id;

View File

@@ -83,10 +83,12 @@ void ProgramMetadata::Print() const {
auto address_space = "Unknown";
switch (npdm_header.address_space_type) {
case ProgramAddressSpaceType::Is64Bit:
case ProgramAddressSpaceType::Is36Bit:
case ProgramAddressSpaceType::Is39Bit:
address_space = "64-bit";
break;
case ProgramAddressSpaceType::Is32Bit:
case ProgramAddressSpaceType::Is32BitNoMap:
address_space = "32-bit";
break;
}

View File

@@ -17,8 +17,10 @@ enum class ResultStatus : u16;
namespace FileSys {
enum class ProgramAddressSpaceType : u8 {
Is64Bit = 1,
Is32Bit = 2,
Is32Bit = 0,
Is36Bit = 1,
Is32BitNoMap = 2,
Is39Bit = 3,
};
enum class ProgramFilePermission : u64 {

View File

@@ -125,7 +125,7 @@ VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
if (concat.empty())
return nullptr;
file = FileSys::ConcatenateFiles(concat, concat.front()->GetName());
file = ConcatenatedVfsFile::MakeConcatenatedFile(concat, concat.front()->GetName());
}
return file;

View File

@@ -129,12 +129,12 @@ VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) {
return out;
}
VirtualFile CreateRomFS(VirtualDir dir) {
VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) {
if (dir == nullptr)
return nullptr;
RomFSBuildContext ctx{dir};
return ConcatenateFiles<0>(ctx.Build(), dir->GetName());
RomFSBuildContext ctx{dir, ext};
return ConcatenatedVfsFile::MakeConcatenatedFile(0, ctx.Build(), dir->GetName());
}
} // namespace FileSys

View File

@@ -44,6 +44,6 @@ VirtualDir ExtractRomFS(VirtualFile file,
// Converts a VFS filesystem into a RomFS binary
// Returns nullptr on failure
VirtualFile CreateRomFS(VirtualDir dir);
VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext = nullptr);
} // namespace FileSys

View File

@@ -30,45 +30,49 @@ RomFSFactory::RomFSFactory(Loader::AppLoader& app_loader) {
RomFSFactory::~RomFSFactory() = default;
void RomFSFactory::SetPackedUpdate(VirtualFile update_raw) {
this->update_raw = std::move(update_raw);
}
ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() {
if (!updatable)
return MakeResult<VirtualFile>(file);
const PatchManager patch_manager(Core::CurrentProcess()->program_id);
return MakeResult<VirtualFile>(patch_manager.PatchRomFS(file, ivfc_offset));
const PatchManager patch_manager(Core::CurrentProcess()->GetTitleID());
return MakeResult<VirtualFile>(
patch_manager.PatchRomFS(file, ivfc_offset, ContentRecordType::Program, update_raw));
}
ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, ContentRecordType type) {
std::shared_ptr<NCA> res;
switch (storage) {
case StorageId::NandSystem: {
const auto res = Service::FileSystem::GetSystemNANDContents()->GetEntry(title_id, type);
if (res == nullptr) {
// TODO(DarkLordZach): Find the right error code to use here
return ResultCode(-1);
}
const auto romfs = res->GetRomFS();
if (romfs == nullptr) {
// TODO(DarkLordZach): Find the right error code to use here
return ResultCode(-1);
}
return MakeResult<VirtualFile>(romfs);
}
case StorageId::NandUser: {
const auto res = Service::FileSystem::GetUserNANDContents()->GetEntry(title_id, type);
if (res == nullptr) {
// TODO(DarkLordZach): Find the right error code to use here
return ResultCode(-1);
}
const auto romfs = res->GetRomFS();
if (romfs == nullptr) {
// TODO(DarkLordZach): Find the right error code to use here
return ResultCode(-1);
}
return MakeResult<VirtualFile>(romfs);
}
case StorageId::None:
res = Service::FileSystem::GetUnionContents()->GetEntry(title_id, type);
break;
case StorageId::NandSystem:
res = Service::FileSystem::GetSystemNANDContents()->GetEntry(title_id, type);
break;
case StorageId::NandUser:
res = Service::FileSystem::GetUserNANDContents()->GetEntry(title_id, type);
break;
case StorageId::SdCard:
res = Service::FileSystem::GetSDMCContents()->GetEntry(title_id, type);
break;
default:
UNIMPLEMENTED_MSG("Unimplemented storage_id={:02X}", static_cast<u8>(storage));
}
if (res == nullptr) {
// TODO(DarkLordZach): Find the right error code to use here
return ResultCode(-1);
}
const auto romfs = res->GetRomFS();
if (romfs == nullptr) {
// TODO(DarkLordZach): Find the right error code to use here
return ResultCode(-1);
}
return MakeResult<VirtualFile>(romfs);
}
} // namespace FileSys

View File

@@ -32,11 +32,13 @@ public:
explicit RomFSFactory(Loader::AppLoader& app_loader);
~RomFSFactory();
void SetPackedUpdate(VirtualFile update_raw);
ResultVal<VirtualFile> OpenCurrentProcess();
ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type);
private:
VirtualFile file;
VirtualFile update_raw;
bool updatable;
u64 ivfc_offset;
};

View File

@@ -81,7 +81,7 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
// According to switchbrew, if a save is of type SaveData and the title id field is 0, it should
// be interpreted as the title id of the current process.
if (type == SaveDataType::SaveData && title_id == 0)
title_id = Core::CurrentProcess()->program_id;
title_id = Core::CurrentProcess()->GetTitleID();
std::string out;

View File

@@ -18,6 +18,39 @@
#include "core/loader/loader.h"
namespace FileSys {
namespace {
void SetTicketKeys(const std::vector<VirtualFile>& files) {
Core::Crypto::KeyManager keys;
for (const auto& ticket_file : files) {
if (ticket_file == nullptr) {
continue;
}
if (ticket_file->GetExtension() != "tik") {
continue;
}
if (ticket_file->GetSize() <
Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET + sizeof(Core::Crypto::Key128)) {
continue;
}
Core::Crypto::Key128 key{};
ticket_file->Read(key.data(), key.size(), Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET);
// We get the name without the extension in order to create the rights ID.
std::string name_only(ticket_file->GetName());
name_only.erase(name_only.size() - 4);
const auto rights_id_raw = Common::HexStringToArray<16>(name_only);
u128 rights_id;
std::memcpy(rights_id.data(), rights_id_raw.data(), sizeof(u128));
keys.SetKey(Core::Crypto::S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
}
}
} // Anonymous namespace
NSP::NSP(VirtualFile file_)
: file(std::move(file_)), status{Loader::ResultStatus::Success},
pfs(std::make_shared<PartitionFilesystem>(file)) {
@@ -26,83 +59,16 @@ NSP::NSP(VirtualFile file_)
return;
}
const auto files = pfs->GetFiles();
if (IsDirectoryExeFS(pfs)) {
extracted = true;
exefs = pfs;
const auto& files = pfs->GetFiles();
const auto romfs_iter =
std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& file) {
return file->GetName().find(".romfs") != std::string::npos;
});
if (romfs_iter != files.end())
romfs = *romfs_iter;
InitializeExeFSAndRomFS(files);
return;
}
extracted = false;
const auto files = pfs->GetFiles();
Core::Crypto::KeyManager keys;
for (const auto& ticket_file : files) {
if (ticket_file->GetExtension() == "tik") {
if (ticket_file == nullptr ||
ticket_file->GetSize() <
Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET + sizeof(Core::Crypto::Key128)) {
continue;
}
Core::Crypto::Key128 key{};
ticket_file->Read(key.data(), key.size(), Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET);
std::string_view name_only(ticket_file->GetName());
name_only.remove_suffix(4);
const auto rights_id_raw = Common::HexStringToArray<16>(name_only);
u128 rights_id;
std::memcpy(rights_id.data(), rights_id_raw.data(), sizeof(u128));
keys.SetKey(Core::Crypto::S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
}
}
for (const auto& outer_file : files) {
if (outer_file->GetName().substr(outer_file->GetName().size() - 9) == ".cnmt.nca") {
const auto nca = std::make_shared<NCA>(outer_file);
if (nca->GetStatus() != Loader::ResultStatus::Success) {
program_status[nca->GetTitleId()] = nca->GetStatus();
continue;
}
const auto section0 = nca->GetSubdirectories()[0];
for (const auto& inner_file : section0->GetFiles()) {
if (inner_file->GetExtension() != "cnmt")
continue;
const CNMT cnmt(inner_file);
auto& ncas_title = ncas[cnmt.GetTitleID()];
ncas_title[ContentRecordType::Meta] = nca;
for (const auto& rec : cnmt.GetContentRecords()) {
const auto id_string = Common::HexArrayToString(rec.nca_id, false);
const auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string));
if (next_file == nullptr) {
LOG_WARNING(Service_FS,
"NCA with ID {}.nca is listed in content metadata, but cannot "
"be found in PFS. NSP appears to be corrupted.",
id_string);
continue;
}
auto next_nca = std::make_shared<NCA>(next_file);
if (next_nca->GetType() == NCAContentType::Program)
program_status[cnmt.GetTitleID()] = next_nca->GetStatus();
if (next_nca->GetStatus() == Loader::ResultStatus::Success)
ncas_title[rec.type] = std::move(next_nca);
}
break;
}
}
}
SetTicketKeys(files);
ReadNCAs(files);
}
NSP::~NSP() = default;
@@ -242,4 +208,66 @@ VirtualDir NSP::GetParentDirectory() const {
bool NSP::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
return false;
}
void NSP::InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files) {
exefs = pfs;
const auto romfs_iter = std::find_if(files.begin(), files.end(), [](const VirtualFile& file) {
return file->GetName().rfind(".romfs") != std::string::npos;
});
if (romfs_iter == files.end()) {
return;
}
romfs = *romfs_iter;
}
void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
for (const auto& outer_file : files) {
if (outer_file->GetName().substr(outer_file->GetName().size() - 9) != ".cnmt.nca") {
continue;
}
const auto nca = std::make_shared<NCA>(outer_file);
if (nca->GetStatus() != Loader::ResultStatus::Success) {
program_status[nca->GetTitleId()] = nca->GetStatus();
continue;
}
const auto section0 = nca->GetSubdirectories()[0];
for (const auto& inner_file : section0->GetFiles()) {
if (inner_file->GetExtension() != "cnmt")
continue;
const CNMT cnmt(inner_file);
auto& ncas_title = ncas[cnmt.GetTitleID()];
ncas_title[ContentRecordType::Meta] = nca;
for (const auto& rec : cnmt.GetContentRecords()) {
const auto id_string = Common::HexArrayToString(rec.nca_id, false);
const auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string));
if (next_file == nullptr) {
LOG_WARNING(Service_FS,
"NCA with ID {}.nca is listed in content metadata, but cannot "
"be found in PFS. NSP appears to be corrupted.",
id_string);
continue;
}
auto next_nca = std::make_shared<NCA>(next_file);
if (next_nca->GetType() == NCAContentType::Program)
program_status[cnmt.GetTitleID()] = next_nca->GetStatus();
if (next_nca->GetStatus() == Loader::ResultStatus::Success ||
(next_nca->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS &&
(cnmt.GetTitleID() & 0x800) != 0)) {
ncas_title[rec.type] = std::move(next_nca);
}
}
break;
}
}
}
} // namespace FileSys

View File

@@ -59,9 +59,12 @@ protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
private:
void InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files);
void ReadNCAs(const std::vector<VirtualFile>& files);
VirtualFile file;
bool extracted;
bool extracted = false;
Loader::ResultStatus status;
std::map<u64, Loader::ResultStatus> program_status;

View File

@@ -463,14 +463,14 @@ bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, std::size_t
return true;
}
bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, size_t block_size) {
bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, std::size_t block_size) {
if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
return false;
if (!dest->Resize(src->GetSize()))
return false;
std::vector<u8> temp(std::min(block_size, src->GetSize()));
for (size_t i = 0; i < src->GetSize(); i += block_size) {
for (std::size_t i = 0; i < src->GetSize(); i += block_size) {
const auto read = std::min(block_size, src->GetSize() - i);
const auto block = src->Read(temp.data(), read, i);
@@ -481,7 +481,7 @@ bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, size_t block_si
return true;
}
bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, size_t block_size) {
bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, std::size_t block_size) {
if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
return false;

View File

@@ -315,18 +315,19 @@ public:
bool Rename(std::string_view name) override;
};
// Compare the two files, byte-for-byte, in increments specificed by block_size
bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, size_t block_size = 0x1000);
// Compare the two files, byte-for-byte, in increments specified by block_size
bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2,
std::size_t block_size = 0x1000);
// A method that copies the raw data between two different implementations of VirtualFile. If you
// are using the same implementation, it is probably better to use the Copy method in the parent
// directory of src/dest.
bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, size_t block_size = 0x1000);
bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, std::size_t block_size = 0x1000);
// A method that performs a similar function to VfsRawCopy above, but instead copies entire
// directories. It suffers the same performance penalties as above and an implementation-specific
// Copy should always be preferred.
bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, size_t block_size = 0x1000);
bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, std::size_t block_size = 0x1000);
// Checks if the directory at path relative to rel exists. If it does, returns that. If it does not
// it attempts to create it and returns the new dir or nullptr on failure.

View File

@@ -7,6 +7,7 @@
#include "common/assert.h"
#include "core/file_sys/vfs_concat.h"
#include "core/file_sys/vfs_static.h"
namespace FileSys {
@@ -22,15 +23,6 @@ static bool VerifyConcatenationMapContinuity(const std::map<u64, VirtualFile>& m
return map.begin()->first == 0;
}
VirtualFile ConcatenateFiles(std::vector<VirtualFile> files, std::string name) {
if (files.empty())
return nullptr;
if (files.size() == 1)
return files[0];
return std::shared_ptr<VfsFile>(new ConcatenatedVfsFile(std::move(files), std::move(name)));
}
ConcatenatedVfsFile::ConcatenatedVfsFile(std::vector<VirtualFile> files_, std::string name)
: name(std::move(name)) {
std::size_t next_offset = 0;
@@ -47,6 +39,41 @@ ConcatenatedVfsFile::ConcatenatedVfsFile(std::map<u64, VirtualFile> files_, std:
ConcatenatedVfsFile::~ConcatenatedVfsFile() = default;
VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(std::vector<VirtualFile> files,
std::string name) {
if (files.empty())
return nullptr;
if (files.size() == 1)
return files[0];
return std::shared_ptr<VfsFile>(new ConcatenatedVfsFile(std::move(files), std::move(name)));
}
VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(u8 filler_byte,
std::map<u64, VirtualFile> files,
std::string name) {
if (files.empty())
return nullptr;
if (files.size() == 1)
return files.begin()->second;
const auto last_valid = --files.end();
for (auto iter = files.begin(); iter != last_valid;) {
const auto old = iter++;
if (old->first + old->second->GetSize() != iter->first) {
files.emplace(old->first + old->second->GetSize(),
std::make_shared<StaticVfsFile>(filler_byte, iter->first - old->first -
old->second->GetSize()));
}
}
// Ensure the map starts at offset 0 (start of file), otherwise pad to fill.
if (files.begin()->first != 0)
files.emplace(0, std::make_shared<StaticVfsFile>(filler_byte, files.begin()->first));
return std::shared_ptr<VfsFile>(new ConcatenatedVfsFile(std::move(files), std::move(name)));
}
std::string ConcatenatedVfsFile::GetName() const {
if (files.empty())
return "";

View File

@@ -7,26 +7,27 @@
#include <map>
#include <memory>
#include <string_view>
#include <boost/container/flat_map.hpp>
#include "core/file_sys/vfs.h"
#include "core/file_sys/vfs_static.h"
namespace FileSys {
// Class that wraps multiple vfs files and concatenates them, making reads seamless. Currently
// read-only.
class ConcatenatedVfsFile : public VfsFile {
friend VirtualFile ConcatenateFiles(std::vector<VirtualFile> files, std::string name);
template <u8 filler_byte>
friend VirtualFile ConcatenateFiles(std::map<u64, VirtualFile> files, std::string name);
ConcatenatedVfsFile(std::vector<VirtualFile> files, std::string name);
ConcatenatedVfsFile(std::map<u64, VirtualFile> files, std::string name);
public:
~ConcatenatedVfsFile() override;
/// Wrapper function to allow for more efficient handling of files.size() == 0, 1 cases.
static VirtualFile MakeConcatenatedFile(std::vector<VirtualFile> files, std::string name);
/// Convenience function that turns a map of offsets to files into a concatenated file, filling
/// gaps with a given filler byte.
static VirtualFile MakeConcatenatedFile(u8 filler_byte, std::map<u64, VirtualFile> files,
std::string name);
std::string GetName() const override;
std::size_t GetSize() const override;
bool Resize(std::size_t new_size) override;
@@ -43,33 +44,4 @@ private:
std::string name;
};
// Wrapper function to allow for more efficient handling of files.size() == 0, 1 cases.
VirtualFile ConcatenateFiles(std::vector<VirtualFile> files, std::string name);
// Convenience function that turns a map of offsets to files into a concatenated file, filling gaps
// with template parameter.
template <u8 filler_byte>
VirtualFile ConcatenateFiles(std::map<u64, VirtualFile> files, std::string name) {
if (files.empty())
return nullptr;
if (files.size() == 1)
return files.begin()->second;
const auto last_valid = --files.end();
for (auto iter = files.begin(); iter != last_valid;) {
const auto old = iter++;
if (old->first + old->second->GetSize() != iter->first) {
files.emplace(old->first + old->second->GetSize(),
std::make_shared<StaticVfsFile<filler_byte>>(iter->first - old->first -
old->second->GetSize()));
}
}
// Ensure the map starts at offset 0 (start of file), otherwise pad to fill.
if (files.begin()->first != 0)
files.emplace(0, std::make_shared<StaticVfsFile<filler_byte>>(files.begin()->first));
return std::shared_ptr<VfsFile>(new ConcatenatedVfsFile(std::move(files), std::move(name)));
}
} // namespace FileSys

View File

@@ -8,7 +8,13 @@
namespace FileSys {
VirtualDir LayerDirectories(std::vector<VirtualDir> dirs, std::string name) {
LayeredVfsDirectory::LayeredVfsDirectory(std::vector<VirtualDir> dirs, std::string name)
: dirs(std::move(dirs)), name(std::move(name)) {}
LayeredVfsDirectory::~LayeredVfsDirectory() = default;
VirtualDir LayeredVfsDirectory::MakeLayeredDirectory(std::vector<VirtualDir> dirs,
std::string name) {
if (dirs.empty())
return nullptr;
if (dirs.size() == 1)
@@ -17,11 +23,6 @@ VirtualDir LayerDirectories(std::vector<VirtualDir> dirs, std::string name) {
return std::shared_ptr<VfsDirectory>(new LayeredVfsDirectory(std::move(dirs), std::move(name)));
}
LayeredVfsDirectory::LayeredVfsDirectory(std::vector<VirtualDir> dirs, std::string name)
: dirs(std::move(dirs)), name(std::move(name)) {}
LayeredVfsDirectory::~LayeredVfsDirectory() = default;
std::shared_ptr<VfsFile> LayeredVfsDirectory::GetFileRelative(std::string_view path) const {
for (const auto& layer : dirs) {
const auto file = layer->GetFileRelative(path);
@@ -41,7 +42,7 @@ std::shared_ptr<VfsDirectory> LayeredVfsDirectory::GetDirectoryRelative(
out.push_back(std::move(dir));
}
return LayerDirectories(std::move(out));
return MakeLayeredDirectory(std::move(out));
}
std::shared_ptr<VfsFile> LayeredVfsDirectory::GetFile(std::string_view name) const {

View File

@@ -9,20 +9,18 @@
namespace FileSys {
// Wrapper function to allow for more efficient handling of dirs.size() == 0, 1 cases.
VirtualDir LayerDirectories(std::vector<VirtualDir> dirs, std::string name = "");
// Class that stacks multiple VfsDirectories on top of each other, attempting to read from the first
// one and falling back to the one after. The highest priority directory (overwrites all others)
// should be element 0 in the dirs vector.
class LayeredVfsDirectory : public VfsDirectory {
friend VirtualDir LayerDirectories(std::vector<VirtualDir> dirs, std::string name);
LayeredVfsDirectory(std::vector<VirtualDir> dirs, std::string name);
public:
~LayeredVfsDirectory() override;
/// Wrapper function to allow for more efficient handling of dirs.size() == 0, 1 cases.
static VirtualDir MakeLayeredDirectory(std::vector<VirtualDir> dirs, std::string name = "");
std::shared_ptr<VfsFile> GetFileRelative(std::string_view path) const override;
std::shared_ptr<VfsDirectory> GetDirectoryRelative(std::string_view path) const override;
std::shared_ptr<VfsFile> GetFile(std::string_view name) const override;

View File

@@ -12,21 +12,21 @@
namespace FileSys {
template <u8 value>
class StaticVfsFile : public VfsFile {
public:
explicit StaticVfsFile(size_t size = 0, std::string name = "", VirtualDir parent = nullptr)
: size(size), name(std::move(name)), parent(std::move(parent)) {}
explicit StaticVfsFile(u8 value, std::size_t size = 0, std::string name = "",
VirtualDir parent = nullptr)
: value{value}, size{size}, name{std::move(name)}, parent{std::move(parent)} {}
std::string GetName() const override {
return name;
}
size_t GetSize() const override {
std::size_t GetSize() const override {
return size;
}
bool Resize(size_t new_size) override {
bool Resize(std::size_t new_size) override {
size = new_size;
return true;
}
@@ -43,23 +43,23 @@ public:
return true;
}
size_t Read(u8* data, size_t length, size_t offset) const override {
std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override {
const auto read = std::min(length, size - offset);
std::fill(data, data + read, value);
return read;
}
size_t Write(const u8* data, size_t length, size_t offset) override {
std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override {
return 0;
}
boost::optional<u8> ReadByte(size_t offset) const override {
boost::optional<u8> ReadByte(std::size_t offset) const override {
if (offset < size)
return value;
return boost::none;
}
std::vector<u8> ReadBytes(size_t length, size_t offset) const override {
std::vector<u8> ReadBytes(std::size_t length, std::size_t offset) const override {
const auto read = std::min(length, size - offset);
return std::vector<u8>(read, value);
}
@@ -70,7 +70,8 @@ public:
}
private:
size_t size;
u8 value;
std::size_t size;
std::string name;
VirtualDir parent;
};

View File

@@ -9,7 +9,7 @@
namespace FileSys {
VectorVfsFile::VectorVfsFile(std::vector<u8> initial_data, std::string name, VirtualDir parent)
: data(std::move(initial_data)), name(std::move(name)), parent(std::move(parent)) {}
: data(std::move(initial_data)), parent(std::move(parent)), name(std::move(name)) {}
VectorVfsFile::~VectorVfsFile() = default;
@@ -38,13 +38,13 @@ bool VectorVfsFile::IsReadable() const {
return true;
}
size_t VectorVfsFile::Read(u8* data_, size_t length, size_t offset) const {
std::size_t VectorVfsFile::Read(u8* data_, std::size_t length, std::size_t offset) const {
const auto read = std::min(length, data.size() - offset);
std::memcpy(data_, data.data() + offset, read);
return read;
}
size_t VectorVfsFile::Write(const u8* data_, size_t length, size_t offset) {
std::size_t VectorVfsFile::Write(const u8* data_, std::size_t length, std::size_t offset) {
if (offset + length > data.size())
data.resize(offset + length);
const auto write = std::min(length, data.size() - offset);

View File

@@ -16,13 +16,13 @@ public:
~VectorVfsFile() override;
std::string GetName() const override;
size_t GetSize() const override;
bool Resize(size_t new_size) override;
std::size_t GetSize() const override;
bool Resize(std::size_t new_size) override;
std::shared_ptr<VfsDirectory> GetContainingDirectory() const override;
bool IsWritable() const override;
bool IsReadable() const override;
size_t Read(u8* data, size_t length, size_t offset) const override;
size_t Write(const u8* data, size_t length, size_t offset) override;
std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override;
std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override;
bool Rename(std::string_view name) override;
virtual void Assign(std::vector<u8> new_data);

View File

@@ -37,7 +37,9 @@
#include "core/core.h"
#include "core/core_cpu.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/loader/loader.h"
#include "core/memory.h"
@@ -207,7 +209,7 @@ static Kernel::Thread* FindThreadById(int id) {
for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList();
for (auto& thread : threads) {
if (thread->GetThreadId() == static_cast<u32>(id)) {
if (thread->GetThreadID() == static_cast<u32>(id)) {
current_core = core;
return thread.get();
}
@@ -221,16 +223,18 @@ static u64 RegRead(std::size_t id, Kernel::Thread* thread = nullptr) {
return 0;
}
const auto& thread_context = thread->GetContext();
if (id < SP_REGISTER) {
return thread->context.cpu_registers[id];
return thread_context.cpu_registers[id];
} else if (id == SP_REGISTER) {
return thread->context.sp;
return thread_context.sp;
} else if (id == PC_REGISTER) {
return thread->context.pc;
return thread_context.pc;
} else if (id == PSTATE_REGISTER) {
return thread->context.pstate;
return thread_context.pstate;
} else if (id > PSTATE_REGISTER && id < FPCR_REGISTER) {
return thread->context.vector_registers[id - UC_ARM64_REG_Q0][0];
return thread_context.vector_registers[id - UC_ARM64_REG_Q0][0];
} else {
return 0;
}
@@ -241,16 +245,18 @@ static void RegWrite(std::size_t id, u64 val, Kernel::Thread* thread = nullptr)
return;
}
auto& thread_context = thread->GetContext();
if (id < SP_REGISTER) {
thread->context.cpu_registers[id] = val;
thread_context.cpu_registers[id] = val;
} else if (id == SP_REGISTER) {
thread->context.sp = val;
thread_context.sp = val;
} else if (id == PC_REGISTER) {
thread->context.pc = val;
thread_context.pc = val;
} else if (id == PSTATE_REGISTER) {
thread->context.pstate = val;
thread_context.pstate = static_cast<u32>(val);
} else if (id > PSTATE_REGISTER && id < FPCR_REGISTER) {
thread->context.vector_registers[id - (PSTATE_REGISTER + 1)][0] = val;
thread_context.vector_registers[id - (PSTATE_REGISTER + 1)][0] = val;
}
}
@@ -585,14 +591,15 @@ static void HandleQuery() {
strlen("Xfer:features:read:target.xml:")) == 0) {
SendReply(target_xml);
} else if (strncmp(query, "Offsets", strlen("Offsets")) == 0) {
std::string buffer = fmt::format("TextSeg={:0x}", Memory::PROCESS_IMAGE_VADDR);
const VAddr base_address = Core::CurrentProcess()->VMManager().GetCodeRegionBaseAddress();
std::string buffer = fmt::format("TextSeg={:0x}", base_address);
SendReply(buffer.c_str());
} else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) {
std::string val = "m";
for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList();
for (const auto& thread : threads) {
val += fmt::format("{:x}", thread->GetThreadId());
val += fmt::format("{:x}", thread->GetThreadID());
val += ",";
}
}
@@ -609,7 +616,7 @@ static void HandleQuery() {
for (const auto& thread : threads) {
buffer +=
fmt::format(R"*(<thread id="{:x}" core="{:d}" name="Thread {:x}"></thread>)*",
thread->GetThreadId(), core, thread->GetThreadId());
thread->GetThreadID(), core, thread->GetThreadID());
}
}
buffer += "</threads>";
@@ -690,7 +697,7 @@ static void SendSignal(Kernel::Thread* thread, u32 signal, bool full = true) {
}
if (thread) {
buffer += fmt::format(";thread:{:x};", thread->GetThreadId());
buffer += fmt::format(";thread:{:x};", thread->GetThreadID());
}
SendReply(buffer.c_str());
@@ -854,7 +861,9 @@ static void WriteRegister() {
}
// Update Unicorn context skipping scheduler, no running threads at this point
Core::System::GetInstance().ArmInterface(current_core).LoadContext(current_thread->context);
Core::System::GetInstance()
.ArmInterface(current_core)
.LoadContext(current_thread->GetContext());
SendReply("OK");
}
@@ -883,7 +892,9 @@ static void WriteRegisters() {
}
// Update Unicorn context skipping scheduler, no running threads at this point
Core::System::GetInstance().ArmInterface(current_core).LoadContext(current_thread->context);
Core::System::GetInstance()
.ArmInterface(current_core)
.LoadContext(current_thread->GetContext());
SendReply("OK");
}
@@ -893,11 +904,11 @@ static void ReadMemory() {
static u8 reply[GDB_BUFFER_SIZE - 4];
auto start_offset = command_buffer + 1;
auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
const auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
const VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
start_offset = addr_pos + 1;
u64 len =
const u64 len =
HexToLong(start_offset, static_cast<u64>((command_buffer + command_length) - start_offset));
LOG_DEBUG(Debug_GDBStub, "gdb: addr: {:016X} len: {:016X}", addr, len);
@@ -906,7 +917,9 @@ static void ReadMemory() {
SendReply("E01");
}
if (addr < Memory::PROCESS_IMAGE_VADDR || addr >= Memory::MAP_REGION_VADDR_END) {
const auto& vm_manager = Core::CurrentProcess()->VMManager();
if (addr < vm_manager.GetCodeRegionBaseAddress() ||
addr >= vm_manager.GetMapRegionEndAddress()) {
return SendReply("E00");
}
@@ -955,7 +968,9 @@ static void Step() {
if (command_length > 1) {
RegWrite(PC_REGISTER, GdbHexToLong(command_buffer + 1), current_thread);
// Update Unicorn context skipping scheduler, no running threads at this point
Core::System::GetInstance().ArmInterface(current_core).LoadContext(current_thread->context);
Core::System::GetInstance()
.ArmInterface(current_core)
.LoadContext(current_thread->GetContext());
}
step_loop = true;
halt_loop = true;

View File

@@ -23,13 +23,13 @@ namespace AddressArbiter {
// Performs actual address waiting logic.
static ResultCode WaitForAddress(VAddr address, s64 timeout) {
SharedPtr<Thread> current_thread = GetCurrentThread();
current_thread->arb_wait_address = address;
current_thread->status = ThreadStatus::WaitArb;
current_thread->wakeup_callback = nullptr;
current_thread->SetArbiterWaitAddress(address);
current_thread->SetStatus(ThreadStatus::WaitArb);
current_thread->InvalidateWakeupCallback();
current_thread->WakeAfterDelay(timeout);
Core::System::GetInstance().CpuCore(current_thread->processor_id).PrepareReschedule();
Core::System::GetInstance().CpuCore(current_thread->GetProcessorID()).PrepareReschedule();
return RESULT_TIMEOUT;
}
@@ -39,10 +39,10 @@ static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address)
std::vector<SharedPtr<Thread>>& waiting_threads,
VAddr arb_addr) {
const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
auto& thread_list = scheduler->GetThreadList();
const auto& thread_list = scheduler->GetThreadList();
for (auto& thread : thread_list) {
if (thread->arb_wait_address == arb_addr)
for (const auto& thread : thread_list) {
if (thread->GetArbiterWaitAddress() == arb_addr)
waiting_threads.push_back(thread);
}
};
@@ -57,7 +57,7 @@ static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address)
// Sort them by priority, such that the highest priority ones come first.
std::sort(threads.begin(), threads.end(),
[](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) {
return lhs->current_priority < rhs->current_priority;
return lhs->GetPriority() < rhs->GetPriority();
});
return threads;
@@ -73,9 +73,9 @@ static void WakeThreads(std::vector<SharedPtr<Thread>>& waiting_threads, s32 num
// Signal the waiting threads.
for (std::size_t i = 0; i < last; i++) {
ASSERT(waiting_threads[i]->status == ThreadStatus::WaitArb);
ASSERT(waiting_threads[i]->GetStatus() == ThreadStatus::WaitArb);
waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS);
waiting_threads[i]->arb_wait_address = 0;
waiting_threads[i]->SetArbiterWaitAddress(0);
waiting_threads[i]->ResumeFromWait();
}
}

View File

@@ -17,6 +17,10 @@ namespace Kernel {
ClientPort::ClientPort(KernelCore& kernel) : Object{kernel} {}
ClientPort::~ClientPort() = default;
SharedPtr<ServerPort> ClientPort::GetServerPort() const {
return server_port;
}
ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() {
// Note: Threads do not wait for the server endpoint to call
// AcceptSession before returning from this call.

View File

@@ -30,6 +30,8 @@ public:
return HANDLE_TYPE;
}
SharedPtr<ServerPort> GetServerPort() const;
/**
* Creates a new Session pair, adds the created ServerSession to the associated ServerPort's
* list of pending sessions, and signals the ServerPort, causing any threads

View File

@@ -31,6 +31,7 @@ enum {
TooLarge = 119,
InvalidEnumValue = 120,
NoSuchEntry = 121,
AlreadyRegistered = 122,
InvalidState = 125,
ResourceLimitExceeded = 132,
};
@@ -58,6 +59,7 @@ constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel,
constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle);
constexpr ResultCode ERR_INVALID_PROCESSOR_ID(ErrorModule::Kernel, ErrCodes::InvalidProcessorId);
constexpr ResultCode ERR_INVALID_SIZE(ErrorModule::Kernel, ErrCodes::InvalidSize);
constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorModule::Kernel, ErrCodes::AlreadyRegistered);
constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState);
constexpr ResultCode ERR_INVALID_THREAD_PRIORITY(ErrorModule::Kernel,
ErrCodes::InvalidThreadPriority);

View File

@@ -42,14 +42,14 @@ SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread,
Kernel::SharedPtr<Kernel::Event> event) {
// Put the client thread to sleep until the wait event is signaled or the timeout expires.
thread->wakeup_callback = [context = *this, callback](
thread->SetWakeupCallback([context = *this, callback](
ThreadWakeupReason reason, SharedPtr<Thread> thread,
SharedPtr<WaitObject> object, std::size_t index) mutable -> bool {
ASSERT(thread->status == ThreadStatus::WaitHLEEvent);
ASSERT(thread->GetStatus() == ThreadStatus::WaitHLEEvent);
callback(thread, context, reason);
context.WriteToOutgoingCommandBuffer(*thread);
return true;
};
});
if (!event) {
// Create event if not provided
@@ -59,8 +59,8 @@ SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread,
}
event->Clear();
thread->status = ThreadStatus::WaitHLEEvent;
thread->wait_objects = {event};
thread->SetStatus(ThreadStatus::WaitHLEEvent);
thread->SetWaitObjects({event});
event->AddWaitingThread(thread);
if (timeout > 0) {
@@ -209,7 +209,7 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdb
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread) {
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf;
Memory::ReadBlock(*thread.owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
Memory::ReadBlock(*thread.GetOwnerProcess(), thread.GetTLSAddress(), dst_cmdbuf.data(),
dst_cmdbuf.size() * sizeof(u32));
// The header was already built in the internal command buffer. Attempt to parse it to verify
@@ -268,7 +268,7 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread)
}
// Copy the translated command buffer back into the thread's command buffer area.
Memory::WriteBlock(*thread.owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
Memory::WriteBlock(*thread.GetOwnerProcess(), thread.GetTLSAddress(), dst_cmdbuf.data(),
dst_cmdbuf.size() * sizeof(u32));
return RESULT_SUCCESS;

View File

@@ -46,40 +46,40 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_
bool resume = true;
if (thread->status == ThreadStatus::WaitSynchAny ||
thread->status == ThreadStatus::WaitSynchAll ||
thread->status == ThreadStatus::WaitHLEEvent) {
if (thread->GetStatus() == ThreadStatus::WaitSynchAny ||
thread->GetStatus() == ThreadStatus::WaitSynchAll ||
thread->GetStatus() == ThreadStatus::WaitHLEEvent) {
// Remove the thread from each of its waiting objects' waitlists
for (auto& object : thread->wait_objects) {
for (const auto& object : thread->GetWaitObjects()) {
object->RemoveWaitingThread(thread.get());
}
thread->wait_objects.clear();
thread->ClearWaitObjects();
// Invoke the wakeup callback before clearing the wait objects
if (thread->wakeup_callback) {
resume = thread->wakeup_callback(ThreadWakeupReason::Timeout, thread, nullptr, 0);
if (thread->HasWakeupCallback()) {
resume = thread->InvokeWakeupCallback(ThreadWakeupReason::Timeout, thread, nullptr, 0);
}
}
if (thread->mutex_wait_address != 0 || thread->condvar_wait_address != 0 ||
thread->wait_handle) {
ASSERT(thread->status == ThreadStatus::WaitMutex);
thread->mutex_wait_address = 0;
thread->condvar_wait_address = 0;
thread->wait_handle = 0;
if (thread->GetMutexWaitAddress() != 0 || thread->GetCondVarWaitAddress() != 0 ||
thread->GetWaitHandle() != 0) {
ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex);
thread->SetMutexWaitAddress(0);
thread->SetCondVarWaitAddress(0);
thread->SetWaitHandle(0);
auto lock_owner = thread->lock_owner;
auto* const lock_owner = thread->GetLockOwner();
// Threads waking up by timeout from WaitProcessWideKey do not perform priority inheritance
// and don't have a lock owner unless SignalProcessWideKey was called first and the thread
// wasn't awakened due to the mutex already being acquired.
if (lock_owner) {
if (lock_owner != nullptr) {
lock_owner->RemoveMutexWaiter(thread);
}
}
if (thread->arb_wait_address != 0) {
ASSERT(thread->status == ThreadStatus::WaitArb);
thread->arb_wait_address = 0;
if (thread->GetArbiterWaitAddress() != 0) {
ASSERT(thread->GetStatus() == ThreadStatus::WaitArb);
thread->SetArbiterWaitAddress(0);
}
if (resume) {
@@ -116,7 +116,7 @@ struct KernelCore::Impl {
next_thread_id = 1;
process_list.clear();
current_process.reset();
current_process = nullptr;
handle_table.Clear();
resource_limits.fill(nullptr);
@@ -207,7 +207,7 @@ struct KernelCore::Impl {
// Lists all processes that exist in the current session.
std::vector<SharedPtr<Process>> process_list;
SharedPtr<Process> current_process;
Process* current_process = nullptr;
Kernel::HandleTable handle_table;
std::array<SharedPtr<ResourceLimit>, 4> resource_limits;
@@ -266,15 +266,15 @@ void KernelCore::AppendNewProcess(SharedPtr<Process> process) {
impl->process_list.push_back(std::move(process));
}
void KernelCore::MakeCurrentProcess(SharedPtr<Process> process) {
impl->current_process = std::move(process);
void KernelCore::MakeCurrentProcess(Process* process) {
impl->current_process = process;
}
SharedPtr<Process>& KernelCore::CurrentProcess() {
Process* KernelCore::CurrentProcess() {
return impl->current_process;
}
const SharedPtr<Process>& KernelCore::CurrentProcess() const {
const Process* KernelCore::CurrentProcess() const {
return impl->current_process;
}

View File

@@ -66,13 +66,13 @@ public:
void AppendNewProcess(SharedPtr<Process> process);
/// Makes the given process the new current process.
void MakeCurrentProcess(SharedPtr<Process> process);
void MakeCurrentProcess(Process* process);
/// Retrieves a reference to the current process.
SharedPtr<Process>& CurrentProcess();
/// Retrieves a pointer to the current process.
Process* CurrentProcess();
/// Retrieves a const reference to the current process.
const SharedPtr<Process>& CurrentProcess() const;
/// Retrieves a const pointer to the current process.
const Process* CurrentProcess() const;
/// Adds a port to the named port table
void AddNamedPort(std::string name, SharedPtr<ClientPort> port);

View File

@@ -28,11 +28,11 @@ static std::pair<SharedPtr<Thread>, u32> GetHighestPriorityMutexWaitingThread(
SharedPtr<Thread> highest_priority_thread;
u32 num_waiters = 0;
for (auto& thread : current_thread->wait_mutex_threads) {
if (thread->mutex_wait_address != mutex_addr)
for (const auto& thread : current_thread->GetMutexWaitingThreads()) {
if (thread->GetMutexWaitAddress() != mutex_addr)
continue;
ASSERT(thread->status == ThreadStatus::WaitMutex);
ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex);
++num_waiters;
if (highest_priority_thread == nullptr ||
@@ -47,12 +47,12 @@ static std::pair<SharedPtr<Thread>, u32> GetHighestPriorityMutexWaitingThread(
/// Update the mutex owner field of all threads waiting on the mutex to point to the new owner.
static void TransferMutexOwnership(VAddr mutex_addr, SharedPtr<Thread> current_thread,
SharedPtr<Thread> new_owner) {
auto threads = current_thread->wait_mutex_threads;
for (auto& thread : threads) {
if (thread->mutex_wait_address != mutex_addr)
const auto threads = current_thread->GetMutexWaitingThreads();
for (const auto& thread : threads) {
if (thread->GetMutexWaitAddress() != mutex_addr)
continue;
ASSERT(thread->lock_owner == current_thread);
ASSERT(thread->GetLockOwner() == current_thread);
current_thread->RemoveMutexWaiter(thread);
if (new_owner != thread)
new_owner->AddMutexWaiter(thread);
@@ -84,11 +84,11 @@ ResultCode Mutex::TryAcquire(HandleTable& handle_table, VAddr address, Handle ho
return ERR_INVALID_HANDLE;
// Wait until the mutex is released
GetCurrentThread()->mutex_wait_address = address;
GetCurrentThread()->wait_handle = requesting_thread_handle;
GetCurrentThread()->SetMutexWaitAddress(address);
GetCurrentThread()->SetWaitHandle(requesting_thread_handle);
GetCurrentThread()->status = ThreadStatus::WaitMutex;
GetCurrentThread()->wakeup_callback = nullptr;
GetCurrentThread()->SetStatus(ThreadStatus::WaitMutex);
GetCurrentThread()->InvalidateWakeupCallback();
// Update the lock holder thread's priority to prevent priority inversion.
holding_thread->AddMutexWaiter(GetCurrentThread());
@@ -115,7 +115,7 @@ ResultCode Mutex::Release(VAddr address) {
// Transfer the ownership of the mutex from the previous owner to the new one.
TransferMutexOwnership(address, GetCurrentThread(), thread);
u32 mutex_value = thread->wait_handle;
u32 mutex_value = thread->GetWaitHandle();
if (num_waiters >= 2) {
// Notify the guest that there are still some threads waiting for the mutex
@@ -125,13 +125,13 @@ ResultCode Mutex::Release(VAddr address) {
// Grant the mutex to the next waiting thread and resume it.
Memory::Write32(address, mutex_value);
ASSERT(thread->status == ThreadStatus::WaitMutex);
ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex);
thread->ResumeFromWait();
thread->lock_owner = nullptr;
thread->condvar_wait_address = 0;
thread->mutex_wait_address = 0;
thread->wait_handle = 0;
thread->SetLockOwner(nullptr);
thread->SetCondVarWaitAddress(0);
thread->SetMutexWaitAddress(0);
thread->SetWaitHandle(0);
return RESULT_SUCCESS;
}

View File

@@ -6,7 +6,6 @@
#include <atomic>
#include <string>
#include <utility>
#include <boost/smart_ptr/intrusive_ptr.hpp>
@@ -97,7 +96,7 @@ using SharedPtr = boost::intrusive_ptr<T>;
template <typename T>
inline SharedPtr<T> DynamicObjectCast(SharedPtr<Object> object) {
if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) {
return boost::static_pointer_cast<T>(std::move(object));
return boost::static_pointer_cast<T>(object);
}
return nullptr;
}

View File

@@ -7,10 +7,13 @@
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/file_sys/program_metadata.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/memory.h"
@@ -32,14 +35,22 @@ SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) {
process->name = std::move(name);
process->flags.raw = 0;
process->flags.memory_region.Assign(MemoryRegion::APPLICATION);
process->resource_limit = kernel.ResourceLimitForCategory(ResourceLimitCategory::APPLICATION);
process->status = ProcessStatus::Created;
process->program_id = 0;
process->process_id = kernel.CreateNewProcessID();
process->svc_access_mask.set();
kernel.AppendNewProcess(process);
return process;
}
void Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
program_id = metadata.GetTitleID();
is_64bit_process = metadata.Is64BitProgram();
vm_manager.Reset(metadata.GetAddressSpaceType());
}
void Process::ParseKernelCaps(const u32* kernel_caps, std::size_t len) {
for (std::size_t i = 0; i < len; ++i) {
u32 descriptor = kernel_caps[i];
@@ -117,7 +128,7 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
// TODO(bunnei): This is heap area that should be allocated by the kernel and not mapped as part
// of the user address space.
vm_manager
.MapMemoryBlock(Memory::STACK_AREA_VADDR_END - stack_size,
.MapMemoryBlock(vm_manager.GetTLSIORegionEndAddress() - stack_size,
std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size,
MemoryState::Mapped)
.Unwrap();
@@ -128,6 +139,91 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
Kernel::SetupMainThread(kernel, entry_point, main_thread_priority, *this);
}
void Process::PrepareForTermination() {
status = ProcessStatus::Exited;
const auto stop_threads = [this](const std::vector<SharedPtr<Thread>>& thread_list) {
for (auto& thread : thread_list) {
if (thread->GetOwnerProcess() != this)
continue;
if (thread == GetCurrentThread())
continue;
// TODO(Subv): When are the other running/ready threads terminated?
ASSERT_MSG(thread->GetStatus() == ThreadStatus::WaitSynchAny ||
thread->GetStatus() == ThreadStatus::WaitSynchAll,
"Exiting processes with non-waiting threads is currently unimplemented");
thread->Stop();
}
};
auto& system = Core::System::GetInstance();
stop_threads(system.Scheduler(0)->GetThreadList());
stop_threads(system.Scheduler(1)->GetThreadList());
stop_threads(system.Scheduler(2)->GetThreadList());
stop_threads(system.Scheduler(3)->GetThreadList());
}
/**
* Finds a free location for the TLS section of a thread.
* @param tls_slots The TLS page array of the thread's owner process.
* Returns a tuple of (page, slot, alloc_needed) where:
* page: The index of the first allocated TLS page that has free slots.
* slot: The index of the first free slot in the indicated page.
* alloc_needed: Whether there's a need to allocate a new TLS page (All pages are full).
*/
static std::tuple<std::size_t, std::size_t, bool> FindFreeThreadLocalSlot(
const std::vector<std::bitset<8>>& tls_slots) {
// Iterate over all the allocated pages, and try to find one where not all slots are used.
for (std::size_t page = 0; page < tls_slots.size(); ++page) {
const auto& page_tls_slots = tls_slots[page];
if (!page_tls_slots.all()) {
// We found a page with at least one free slot, find which slot it is
for (std::size_t slot = 0; slot < page_tls_slots.size(); ++slot) {
if (!page_tls_slots.test(slot)) {
return std::make_tuple(page, slot, false);
}
}
}
}
return std::make_tuple(0, 0, true);
}
VAddr Process::MarkNextAvailableTLSSlotAsUsed(Thread& thread) {
auto [available_page, available_slot, needs_allocation] = FindFreeThreadLocalSlot(tls_slots);
const VAddr tls_begin = vm_manager.GetTLSIORegionBaseAddress();
if (needs_allocation) {
tls_slots.emplace_back(0); // The page is completely available at the start
available_page = tls_slots.size() - 1;
available_slot = 0; // Use the first slot in the new page
// Allocate some memory from the end of the linear heap for this region.
auto& tls_memory = thread.GetTLSMemory();
tls_memory->insert(tls_memory->end(), Memory::PAGE_SIZE, 0);
vm_manager.RefreshMemoryBlockMappings(tls_memory.get());
vm_manager.MapMemoryBlock(tls_begin + available_page * Memory::PAGE_SIZE, tls_memory, 0,
Memory::PAGE_SIZE, MemoryState::ThreadLocal);
}
tls_slots[available_page].set(available_slot);
return tls_begin + available_page * Memory::PAGE_SIZE + available_slot * Memory::TLS_ENTRY_SIZE;
}
void Process::FreeTLSSlot(VAddr tls_address) {
const VAddr tls_base = tls_address - vm_manager.GetTLSIORegionBaseAddress();
const VAddr tls_page = tls_base / Memory::PAGE_SIZE;
const VAddr tls_slot = (tls_base % Memory::PAGE_SIZE) / Memory::TLS_ENTRY_SIZE;
tls_slots[tls_page].reset(tls_slot);
}
void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) {
const auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions,
MemoryState memory_state) {
@@ -145,8 +241,8 @@ void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) {
}
ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
if (target < Memory::HEAP_VADDR || target + size > Memory::HEAP_VADDR_END ||
target + size < target) {
if (target < vm_manager.GetHeapRegionBaseAddress() ||
target + size > vm_manager.GetHeapRegionEndAddress() || target + size < target) {
return ERR_INVALID_ADDRESS;
}
@@ -181,8 +277,8 @@ ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission per
}
ResultCode Process::HeapFree(VAddr target, u32 size) {
if (target < Memory::HEAP_VADDR || target + size > Memory::HEAP_VADDR_END ||
target + size < target) {
if (target < vm_manager.GetHeapRegionBaseAddress() ||
target + size > vm_manager.GetHeapRegionEndAddress() || target + size < target) {
return ERR_INVALID_ADDRESS;
}

View File

@@ -17,6 +17,10 @@
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/vm_manager.h"
namespace FileSys {
class ProgramMetadata;
}
namespace Kernel {
class KernelCore;
@@ -131,6 +135,121 @@ public:
return HANDLE_TYPE;
}
/// Gets a reference to the process' memory manager.
Kernel::VMManager& VMManager() {
return vm_manager;
}
/// Gets a const reference to the process' memory manager.
const Kernel::VMManager& VMManager() const {
return vm_manager;
}
/// Gets the current status of the process
ProcessStatus GetStatus() const {
return status;
}
/// Gets the unique ID that identifies this particular process.
u32 GetProcessID() const {
return process_id;
}
/// Gets the title ID corresponding to this process.
u64 GetTitleID() const {
return program_id;
}
/// Gets the resource limit descriptor for this process
ResourceLimit& GetResourceLimit() {
return *resource_limit;
}
/// Gets the resource limit descriptor for this process
const ResourceLimit& GetResourceLimit() const {
return *resource_limit;
}
/// Gets the default CPU ID for this process
u8 GetDefaultProcessorID() const {
return ideal_processor;
}
/// Gets the bitmask of allowed CPUs that this process' threads can run on.
u32 GetAllowedProcessorMask() const {
return allowed_processor_mask;
}
/// Gets the bitmask of allowed thread priorities.
u32 GetAllowedThreadPriorityMask() const {
return allowed_thread_priority_mask;
}
u32 IsVirtualMemoryEnabled() const {
return is_virtual_address_memory_enabled;
}
/// Whether this process is an AArch64 or AArch32 process.
bool Is64BitProcess() const {
return is_64bit_process;
}
/**
* Loads process-specifics configuration info with metadata provided
* by an executable.
*
* @param metadata The provided metadata to load process specific info.
*/
void LoadFromMetadata(const FileSys::ProgramMetadata& metadata);
/**
* Parses a list of kernel capability descriptors (as found in the ExHeader) and applies them
* to this process.
*/
void ParseKernelCaps(const u32* kernel_caps, std::size_t len);
/**
* Applies address space changes and launches the process main thread.
*/
void Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size);
/**
* Prepares a process for termination by stopping all of its threads
* and clearing any other resources.
*/
void PrepareForTermination();
void LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr);
///////////////////////////////////////////////////////////////////////////////////////////////
// Memory Management
// Marks the next available region as used and returns the address of the slot.
VAddr MarkNextAvailableTLSSlotAsUsed(Thread& thread);
// Frees a used TLS slot identified by the given address
void FreeTLSSlot(VAddr tls_address);
ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
ResultCode HeapFree(VAddr target, u32 size);
ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size);
ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size);
private:
explicit Process(KernelCore& kernel);
~Process() override;
/// Memory manager for this process.
Kernel::VMManager vm_manager;
/// Current status of the process
ProcessStatus status;
/// The ID of this process
u32 process_id = 0;
/// Title ID corresponding to the process
u64 program_id;
@@ -140,7 +259,7 @@ public:
/// The process may only call SVCs which have the corresponding bit set.
std::bitset<0x80> svc_access_mask;
/// Maximum size of the handle table for the process.
unsigned int handle_table_size = 0x200;
u32 handle_table_size = 0x200;
/// Special memory ranges mapped into this processes address space. This is used to give
/// processes access to specific I/O regions and device memory.
boost::container::static_vector<AddressMapping, 8> address_mappings;
@@ -154,29 +273,6 @@ public:
u32 allowed_processor_mask = THREADPROCESSORID_DEFAULT_MASK;
u32 allowed_thread_priority_mask = 0xFFFFFFFF;
u32 is_virtual_address_memory_enabled = 0;
/// Current status of the process
ProcessStatus status;
/// The ID of this process
u32 process_id = 0;
/**
* Parses a list of kernel capability descriptors (as found in the ExHeader) and applies them
* to this process.
*/
void ParseKernelCaps(const u32* kernel_caps, std::size_t len);
/**
* Applies address space changes and launches the process main thread.
*/
void Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size);
void LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr);
///////////////////////////////////////////////////////////////////////////////////////////////
// Memory Management
VMManager vm_manager;
// Memory used to back the allocations in the regular heap. A single vector is used to cover
// the entire virtual address space extents that bound the allocations, including any holes.
@@ -196,18 +292,12 @@ public:
/// This vector will grow as more pages are allocated for new threads.
std::vector<std::bitset<8>> tls_slots;
/// Whether or not this process is AArch64, or AArch32.
/// By default, we currently assume this is true, unless otherwise
/// specified by metadata provided to the process during loading.
bool is_64bit_process = true;
std::string name;
ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
ResultCode HeapFree(VAddr target, u32 size);
ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size);
ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size);
private:
explicit Process(KernelCore& kernel);
~Process() override;
};
} // namespace Kernel

View File

@@ -9,7 +9,7 @@
#include "common/logging/log.h"
#include "core/arm/arm_interface.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/scheduler.h"
@@ -17,7 +17,7 @@ namespace Kernel {
std::mutex Scheduler::scheduler_mutex;
Scheduler::Scheduler(Core::ARM_Interface* cpu_core) : cpu_core(cpu_core) {}
Scheduler::Scheduler(Core::ARM_Interface& cpu_core) : cpu_core(cpu_core) {}
Scheduler::~Scheduler() {
for (auto& thread : thread_list) {
@@ -38,10 +38,10 @@ Thread* Scheduler::PopNextReadyThread() {
Thread* next = nullptr;
Thread* thread = GetCurrentThread();
if (thread && thread->status == ThreadStatus::Running) {
if (thread && thread->GetStatus() == ThreadStatus::Running) {
// We have to do better than the current thread.
// This call returns null when that's not possible.
next = ready_queue.pop_first_better(thread->current_priority);
next = ready_queue.pop_first_better(thread->GetPriority());
if (!next) {
// Otherwise just keep going with the current thread
next = thread;
@@ -58,43 +58,43 @@ void Scheduler::SwitchContext(Thread* new_thread) {
// Save context for previous thread
if (previous_thread) {
previous_thread->last_running_ticks = CoreTiming::GetTicks();
cpu_core->SaveContext(previous_thread->context);
cpu_core.SaveContext(previous_thread->GetContext());
// Save the TPIDR_EL0 system register in case it was modified.
previous_thread->tpidr_el0 = cpu_core->GetTPIDR_EL0();
previous_thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
if (previous_thread->status == ThreadStatus::Running) {
if (previous_thread->GetStatus() == ThreadStatus::Running) {
// This is only the case when a reschedule is triggered without the current thread
// yielding execution (i.e. an event triggered, system core time-sliced, etc)
ready_queue.push_front(previous_thread->current_priority, previous_thread);
previous_thread->status = ThreadStatus::Ready;
ready_queue.push_front(previous_thread->GetPriority(), previous_thread);
previous_thread->SetStatus(ThreadStatus::Ready);
}
}
// Load context of new thread
if (new_thread) {
ASSERT_MSG(new_thread->status == ThreadStatus::Ready,
ASSERT_MSG(new_thread->GetStatus() == ThreadStatus::Ready,
"Thread must be ready to become running.");
// Cancel any outstanding wakeup events for this thread
new_thread->CancelWakeupTimer();
auto previous_process = Core::CurrentProcess();
auto* const previous_process = Core::CurrentProcess();
current_thread = new_thread;
ready_queue.remove(new_thread->current_priority, new_thread);
new_thread->status = ThreadStatus::Running;
ready_queue.remove(new_thread->GetPriority(), new_thread);
new_thread->SetStatus(ThreadStatus::Running);
if (previous_process != current_thread->owner_process) {
Core::CurrentProcess() = current_thread->owner_process;
SetCurrentPageTable(&Core::CurrentProcess()->vm_manager.page_table);
auto* const thread_owner_process = current_thread->GetOwnerProcess();
if (previous_process != thread_owner_process) {
Core::System::GetInstance().Kernel().MakeCurrentProcess(thread_owner_process);
SetCurrentPageTable(&Core::CurrentProcess()->VMManager().page_table);
}
cpu_core->LoadContext(new_thread->context);
cpu_core->SetTlsAddress(new_thread->GetTLSAddress());
cpu_core->SetTPIDR_EL0(new_thread->GetTPIDR_EL0());
cpu_core->ClearExclusiveState();
cpu_core.LoadContext(new_thread->GetContext());
cpu_core.SetTlsAddress(new_thread->GetTLSAddress());
cpu_core.SetTPIDR_EL0(new_thread->GetTPIDR_EL0());
cpu_core.ClearExclusiveState();
} else {
current_thread = nullptr;
// Note: We do not reset the current process and current page table when idling because
@@ -136,14 +136,14 @@ void Scheduler::RemoveThread(Thread* thread) {
void Scheduler::ScheduleThread(Thread* thread, u32 priority) {
std::lock_guard<std::mutex> lock(scheduler_mutex);
ASSERT(thread->status == ThreadStatus::Ready);
ASSERT(thread->GetStatus() == ThreadStatus::Ready);
ready_queue.push_back(priority, thread);
}
void Scheduler::UnscheduleThread(Thread* thread, u32 priority) {
std::lock_guard<std::mutex> lock(scheduler_mutex);
ASSERT(thread->status == ThreadStatus::Ready);
ASSERT(thread->GetStatus() == ThreadStatus::Ready);
ready_queue.remove(priority, thread);
}
@@ -151,8 +151,8 @@ void Scheduler::SetThreadPriority(Thread* thread, u32 priority) {
std::lock_guard<std::mutex> lock(scheduler_mutex);
// If thread was ready, adjust queues
if (thread->status == ThreadStatus::Ready)
ready_queue.move(thread, thread->current_priority, priority);
if (thread->GetStatus() == ThreadStatus::Ready)
ready_queue.move(thread, thread->GetPriority(), priority);
else
ready_queue.prepare(priority);
}

View File

@@ -19,7 +19,7 @@ namespace Kernel {
class Scheduler final {
public:
explicit Scheduler(Core::ARM_Interface* cpu_core);
explicit Scheduler(Core::ARM_Interface& cpu_core);
~Scheduler();
/// Returns whether there are any threads that are ready to run.
@@ -72,7 +72,7 @@ private:
SharedPtr<Thread> current_thread = nullptr;
Core::ARM_Interface* cpu_core;
Core::ARM_Interface& cpu_core;
static std::mutex scheduler_mutex;
};

View File

@@ -11,6 +11,7 @@
#include "common/common_types.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/wait_object.h"
#include "core/hle/result.h"
namespace Kernel {

View File

@@ -120,10 +120,10 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {
result = hle_handler->HandleSyncRequest(context);
}
if (thread->status == ThreadStatus::Running) {
if (thread->GetStatus() == ThreadStatus::Running) {
// Put the thread to sleep until the server replies, it will be awoken in
// svcReplyAndReceive for LLE servers.
thread->status = ThreadStatus::WaitIPC;
thread->SetStatus(ThreadStatus::WaitIPC);
if (hle_handler != nullptr) {
// For HLE services, we put the request threads to sleep for a short duration to

View File

@@ -8,6 +8,7 @@
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/memory.h"
@@ -34,11 +35,11 @@ SharedPtr<SharedMemory> SharedMemory::Create(KernelCore& kernel, SharedPtr<Proce
// Refresh the address mappings for the current process.
if (Core::CurrentProcess() != nullptr) {
Core::CurrentProcess()->vm_manager.RefreshMemoryBlockMappings(
Core::CurrentProcess()->VMManager().RefreshMemoryBlockMappings(
shared_memory->backing_block.get());
}
} else {
auto& vm_manager = shared_memory->owner_process->vm_manager;
auto& vm_manager = shared_memory->owner_process->VMManager();
// The memory is already available and mapped in the owner process.
auto vma = vm_manager.FindVMA(address);
@@ -71,7 +72,8 @@ SharedPtr<SharedMemory> SharedMemory::CreateForApplet(
shared_memory->other_permissions = other_permissions;
shared_memory->backing_block = std::move(heap_block);
shared_memory->backing_block_offset = offset;
shared_memory->base_address = Memory::HEAP_VADDR + offset;
shared_memory->base_address =
kernel.CurrentProcess()->VMManager().GetHeapRegionBaseAddress() + offset;
return shared_memory;
}
@@ -105,7 +107,7 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
VAddr target_address = address;
// Map the memory block into the target process
auto result = target_process->vm_manager.MapMemoryBlock(
auto result = target_process->VMManager().MapMemoryBlock(
target_address, backing_block, backing_block_offset, size, MemoryState::Shared);
if (result.Failed()) {
LOG_ERROR(
@@ -115,14 +117,14 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
return result.Code();
}
return target_process->vm_manager.ReprotectRange(target_address, size,
ConvertPermissions(permissions));
return target_process->VMManager().ReprotectRange(target_address, size,
ConvertPermissions(permissions));
}
ResultCode SharedMemory::Unmap(Process* target_process, VAddr address) {
// TODO(Subv): Verify what happens if the application tries to unmap an address that is not
// mapped to a SharedMemory.
return target_process->vm_manager.UnmapRange(address, size);
return target_process->VMManager().UnmapRange(address, size);
}
VMAPermission SharedMemory::ConvertPermissions(MemoryPermission permission) {

View File

@@ -51,8 +51,9 @@ static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) {
}
auto& process = *Core::CurrentProcess();
const VAddr heap_base = process.VMManager().GetHeapRegionBaseAddress();
CASCADE_RESULT(*heap_addr,
process.HeapAllocate(Memory::HEAP_VADDR, heap_size, VMAPermission::ReadWrite));
process.HeapAllocate(heap_base, heap_size, VMAPermission::ReadWrite));
return RESULT_SUCCESS;
}
@@ -155,7 +156,7 @@ static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) {
return ERR_INVALID_HANDLE;
}
*thread_id = thread->GetThreadId();
*thread_id = thread->GetThreadID();
return RESULT_SUCCESS;
}
@@ -169,14 +170,14 @@ static ResultCode GetProcessId(u32* process_id, Handle process_handle) {
return ERR_INVALID_HANDLE;
}
*process_id = process->process_id;
*process_id = process->GetProcessID();
return RESULT_SUCCESS;
}
/// Default thread wakeup callback for WaitSynchronization
static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> thread,
SharedPtr<WaitObject> object, std::size_t index) {
ASSERT(thread->status == ThreadStatus::WaitSynchAny);
ASSERT(thread->GetStatus() == ThreadStatus::WaitSynchAny);
if (reason == ThreadWakeupReason::Timeout) {
thread->SetWaitSynchronizationResult(RESULT_TIMEOUT);
@@ -203,10 +204,10 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
if (handle_count > MaxHandles)
return ResultCode(ErrorModule::Kernel, ErrCodes::TooLarge);
auto thread = GetCurrentThread();
auto* const thread = GetCurrentThread();
using ObjectPtr = SharedPtr<WaitObject>;
std::vector<ObjectPtr> objects(handle_count);
using ObjectPtr = Thread::ThreadWaitObjects::value_type;
Thread::ThreadWaitObjects objects(handle_count);
auto& kernel = Core::System::GetInstance().Kernel();
for (u64 i = 0; i < handle_count; ++i) {
@@ -243,14 +244,14 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
for (auto& object : objects)
object->AddWaitingThread(thread);
thread->wait_objects = std::move(objects);
thread->status = ThreadStatus::WaitSynchAny;
thread->SetWaitObjects(std::move(objects));
thread->SetStatus(ThreadStatus::WaitSynchAny);
// Create an event to wake the thread up after the specified nanosecond delay has passed
thread->WakeAfterDelay(nano_seconds);
thread->wakeup_callback = DefaultThreadWakeupCallback;
thread->SetWakeupCallback(DefaultThreadWakeupCallback);
Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule();
Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
return RESULT_TIMEOUT;
}
@@ -265,7 +266,7 @@ static ResultCode CancelSynchronization(Handle thread_handle) {
return ERR_INVALID_HANDLE;
}
ASSERT(thread->status == ThreadStatus::WaitSynchAny);
ASSERT(thread->GetStatus() == ThreadStatus::WaitSynchAny);
thread->SetWaitSynchronizationResult(
ResultCode(ErrorModule::Kernel, ErrCodes::SynchronizationCanceled));
thread->ResumeFromWait();
@@ -300,13 +301,28 @@ static ResultCode ArbitrateUnlock(VAddr mutex_addr) {
return Mutex::Release(mutex_addr);
}
struct BreakReason {
union {
u64 raw;
BitField<31, 1, u64> dont_kill_application;
};
};
/// Break program execution
static void Break(u64 reason, u64 info1, u64 info2) {
LOG_CRITICAL(
Debug_Emulated,
"Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
reason, info1, info2);
ASSERT(false);
BreakReason break_reason{reason};
if (break_reason.dont_kill_application) {
LOG_ERROR(
Debug_Emulated,
"Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
reason, info1, info2);
} else {
LOG_CRITICAL(
Debug_Emulated,
"Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
reason, info1, info2);
ASSERT(false);
}
}
/// Used to output a message on a debug hardware unit - does nothing on a retail unit
@@ -325,26 +341,27 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id,
info_sub_id, handle);
const auto& vm_manager = Core::CurrentProcess()->vm_manager;
const auto* current_process = Core::CurrentProcess();
const auto& vm_manager = current_process->VMManager();
switch (static_cast<GetInfoType>(info_id)) {
case GetInfoType::AllowedCpuIdBitmask:
*result = Core::CurrentProcess()->allowed_processor_mask;
*result = current_process->GetAllowedProcessorMask();
break;
case GetInfoType::AllowedThreadPrioBitmask:
*result = Core::CurrentProcess()->allowed_thread_priority_mask;
*result = current_process->GetAllowedThreadPriorityMask();
break;
case GetInfoType::MapRegionBaseAddr:
*result = Memory::MAP_REGION_VADDR;
*result = vm_manager.GetMapRegionBaseAddress();
break;
case GetInfoType::MapRegionSize:
*result = Memory::MAP_REGION_SIZE;
*result = vm_manager.GetMapRegionSize();
break;
case GetInfoType::HeapRegionBaseAddr:
*result = Memory::HEAP_VADDR;
*result = vm_manager.GetHeapRegionBaseAddress();
break;
case GetInfoType::HeapRegionSize:
*result = Memory::HEAP_SIZE;
*result = vm_manager.GetHeapRegionSize();
break;
case GetInfoType::TotalMemoryUsage:
*result = vm_manager.GetTotalMemoryUsage();
@@ -359,22 +376,35 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
*result = 0;
break;
case GetInfoType::AddressSpaceBaseAddr:
*result = vm_manager.GetAddressSpaceBaseAddr();
*result = vm_manager.GetCodeRegionBaseAddress();
break;
case GetInfoType::AddressSpaceSize:
*result = vm_manager.GetAddressSpaceSize();
case GetInfoType::AddressSpaceSize: {
const u64 width = vm_manager.GetAddressSpaceWidth();
switch (width) {
case 32:
*result = 0xFFE00000;
break;
case 36:
*result = 0xFF8000000;
break;
case 39:
*result = 0x7FF8000000;
break;
}
break;
}
case GetInfoType::NewMapRegionBaseAddr:
*result = Memory::NEW_MAP_REGION_VADDR;
*result = vm_manager.GetNewMapRegionBaseAddress();
break;
case GetInfoType::NewMapRegionSize:
*result = Memory::NEW_MAP_REGION_SIZE;
*result = vm_manager.GetNewMapRegionSize();
break;
case GetInfoType::IsVirtualAddressMemoryEnabled:
*result = Core::CurrentProcess()->is_virtual_address_memory_enabled;
*result = current_process->IsVirtualMemoryEnabled();
break;
case GetInfoType::TitleId:
*result = Core::CurrentProcess()->program_id;
*result = current_process->GetTitleID();
break;
case GetInfoType::PrivilegedProcessId:
LOG_WARNING(Kernel_SVC,
@@ -400,8 +430,36 @@ static ResultCode SetThreadActivity(Handle handle, u32 unknown) {
}
/// Gets the thread context
static ResultCode GetThreadContext(Handle handle, VAddr addr) {
LOG_WARNING(Kernel_SVC, "(STUBBED) called, handle=0x{:08X}, addr=0x{:X}", handle, addr);
static ResultCode GetThreadContext(VAddr thread_context, Handle handle) {
LOG_DEBUG(Kernel_SVC, "called, context=0x{:08X}, thread=0x{:X}", thread_context, handle);
auto& kernel = Core::System::GetInstance().Kernel();
const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(handle);
if (!thread) {
return ERR_INVALID_HANDLE;
}
const auto* current_process = Core::CurrentProcess();
if (thread->GetOwnerProcess() != current_process) {
return ERR_INVALID_HANDLE;
}
if (thread == GetCurrentThread()) {
return ERR_ALREADY_REGISTERED;
}
Core::ARM_Interface::ThreadContext ctx = thread->GetContext();
// Mask away mode bits, interrupt bits, IL bit, and other reserved bits.
ctx.pstate &= 0xFF0FFE20;
// If 64-bit, we can just write the context registers directly and we're good.
// However, if 32-bit, we have to ensure some registers are zeroed out.
if (!current_process->Is64BitProcess()) {
std::fill(ctx.cpu_registers.begin() + 15, ctx.cpu_registers.end(), 0);
std::fill(ctx.vector_registers.begin() + 16, ctx.vector_registers.end(), u128{});
}
Memory::WriteBlock(thread_context, &ctx, sizeof(ctx));
return RESULT_SUCCESS;
}
@@ -429,21 +487,21 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) {
// Note: The kernel uses the current process's resource limit instead of
// the one from the thread owner's resource limit.
SharedPtr<ResourceLimit>& resource_limit = Core::CurrentProcess()->resource_limit;
if (resource_limit->GetMaxResourceValue(ResourceType::Priority) > priority) {
const ResourceLimit& resource_limit = Core::CurrentProcess()->GetResourceLimit();
if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) {
return ERR_NOT_AUTHORIZED;
}
thread->SetPriority(priority);
Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule();
Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
return RESULT_SUCCESS;
}
/// Get which CPU core is executing the current thread
static u32 GetCurrentProcessorNumber() {
LOG_TRACE(Kernel_SVC, "called");
return GetCurrentThread()->processor_id;
return GetCurrentThread()->GetProcessorID();
}
static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size,
@@ -473,7 +531,7 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
return ERR_INVALID_HANDLE;
}
return shared_memory->Map(Core::CurrentProcess().get(), addr, permissions_type,
return shared_memory->Map(Core::CurrentProcess(), addr, permissions_type,
MemoryPermission::DontCare);
}
@@ -492,7 +550,7 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64
auto& kernel = Core::System::GetInstance().Kernel();
auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle);
return shared_memory->Unmap(Core::CurrentProcess().get(), addr);
return shared_memory->Unmap(Core::CurrentProcess(), addr);
}
/// Query process memory
@@ -504,9 +562,9 @@ static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_i
if (!process) {
return ERR_INVALID_HANDLE;
}
auto vma = process->vm_manager.FindVMA(addr);
auto vma = process->VMManager().FindVMA(addr);
memory_info->attributes = 0;
if (vma == Core::CurrentProcess()->vm_manager.vma_map.end()) {
if (vma == Core::CurrentProcess()->VMManager().vma_map.end()) {
memory_info->base_address = 0;
memory_info->permission = static_cast<u32>(VMAPermission::None);
memory_info->size = 0;
@@ -530,35 +588,13 @@ static ResultCode QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, VAdd
/// Exits the current process
static void ExitProcess() {
LOG_INFO(Kernel_SVC, "Process {} exiting", Core::CurrentProcess()->process_id);
auto* current_process = Core::CurrentProcess();
ASSERT_MSG(Core::CurrentProcess()->status == ProcessStatus::Running,
LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID());
ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running,
"Process has already exited");
Core::CurrentProcess()->status = ProcessStatus::Exited;
auto stop_threads = [](const std::vector<SharedPtr<Thread>>& thread_list) {
for (auto& thread : thread_list) {
if (thread->owner_process != Core::CurrentProcess())
continue;
if (thread == GetCurrentThread())
continue;
// TODO(Subv): When are the other running/ready threads terminated?
ASSERT_MSG(thread->status == ThreadStatus::WaitSynchAny ||
thread->status == ThreadStatus::WaitSynchAll,
"Exiting processes with non-waiting threads is currently unimplemented");
thread->Stop();
}
};
auto& system = Core::System::GetInstance();
stop_threads(system.Scheduler(0)->GetThreadList());
stop_threads(system.Scheduler(1)->GetThreadList());
stop_threads(system.Scheduler(2)->GetThreadList());
stop_threads(system.Scheduler(3)->GetThreadList());
current_process->PrepareForTermination();
// Kill the current thread
GetCurrentThread()->Stop();
@@ -575,14 +611,14 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
return ERR_INVALID_THREAD_PRIORITY;
}
SharedPtr<ResourceLimit>& resource_limit = Core::CurrentProcess()->resource_limit;
if (resource_limit->GetMaxResourceValue(ResourceType::Priority) > priority) {
const ResourceLimit& resource_limit = Core::CurrentProcess()->GetResourceLimit();
if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) {
return ERR_NOT_AUTHORIZED;
}
if (processor_id == THREADPROCESSORID_DEFAULT) {
// Set the target CPU to the one specified in the process' exheader.
processor_id = Core::CurrentProcess()->ideal_processor;
processor_id = Core::CurrentProcess()->GetDefaultProcessorID();
ASSERT(processor_id != THREADPROCESSORID_DEFAULT);
}
@@ -600,11 +636,15 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
auto& kernel = Core::System::GetInstance().Kernel();
CASCADE_RESULT(SharedPtr<Thread> thread,
Thread::Create(kernel, name, entry_point, priority, arg, processor_id, stack_top,
Core::CurrentProcess()));
CASCADE_RESULT(thread->guest_handle, kernel.HandleTable().Create(thread));
*out_handle = thread->guest_handle;
*Core::CurrentProcess()));
const auto new_guest_handle = kernel.HandleTable().Create(thread);
if (new_guest_handle.Failed()) {
return new_guest_handle.Code();
}
thread->SetGuestHandle(*new_guest_handle);
*out_handle = *new_guest_handle;
Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule();
Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
LOG_TRACE(Kernel_SVC,
"called entrypoint=0x{:08X} ({}), arg=0x{:08X}, stacktop=0x{:08X}, "
@@ -624,10 +664,10 @@ static ResultCode StartThread(Handle thread_handle) {
return ERR_INVALID_HANDLE;
}
ASSERT(thread->status == ThreadStatus::Dormant);
ASSERT(thread->GetStatus() == ThreadStatus::Dormant);
thread->ResumeFromWait();
Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule();
Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
return RESULT_SUCCESS;
}
@@ -673,17 +713,17 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var
CASCADE_CODE(Mutex::Release(mutex_addr));
SharedPtr<Thread> current_thread = GetCurrentThread();
current_thread->condvar_wait_address = condition_variable_addr;
current_thread->mutex_wait_address = mutex_addr;
current_thread->wait_handle = thread_handle;
current_thread->status = ThreadStatus::WaitMutex;
current_thread->wakeup_callback = nullptr;
current_thread->SetCondVarWaitAddress(condition_variable_addr);
current_thread->SetMutexWaitAddress(mutex_addr);
current_thread->SetWaitHandle(thread_handle);
current_thread->SetStatus(ThreadStatus::WaitMutex);
current_thread->InvalidateWakeupCallback();
current_thread->WakeAfterDelay(nano_seconds);
// Note: Deliberately don't attempt to inherit the lock owner's priority.
Core::System::GetInstance().CpuCore(current_thread->processor_id).PrepareReschedule();
Core::System::GetInstance().CpuCore(current_thread->GetProcessorID()).PrepareReschedule();
return RESULT_SUCCESS;
}
@@ -692,14 +732,14 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x{:X}, target=0x{:08X}",
condition_variable_addr, target);
auto RetrieveWaitingThreads = [](std::size_t core_index,
std::vector<SharedPtr<Thread>>& waiting_threads,
VAddr condvar_addr) {
const auto RetrieveWaitingThreads = [](std::size_t core_index,
std::vector<SharedPtr<Thread>>& waiting_threads,
VAddr condvar_addr) {
const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
auto& thread_list = scheduler->GetThreadList();
const auto& thread_list = scheduler->GetThreadList();
for (auto& thread : thread_list) {
if (thread->condvar_wait_address == condvar_addr)
for (const auto& thread : thread_list) {
if (thread->GetCondVarWaitAddress() == condvar_addr)
waiting_threads.push_back(thread);
}
};
@@ -713,7 +753,7 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
// Sort them by priority, such that the highest priority ones come first.
std::sort(waiting_threads.begin(), waiting_threads.end(),
[](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) {
return lhs->current_priority < rhs->current_priority;
return lhs->GetPriority() < rhs->GetPriority();
});
// Only process up to 'target' threads, unless 'target' is -1, in which case process
@@ -729,7 +769,7 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
for (std::size_t index = 0; index < last; ++index) {
auto& thread = waiting_threads[index];
ASSERT(thread->condvar_wait_address == condition_variable_addr);
ASSERT(thread->GetCondVarWaitAddress() == condition_variable_addr);
std::size_t current_core = Core::System::GetInstance().CurrentCoreIndex();
@@ -738,42 +778,43 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
// Atomically read the value of the mutex.
u32 mutex_val = 0;
do {
monitor.SetExclusive(current_core, thread->mutex_wait_address);
monitor.SetExclusive(current_core, thread->GetMutexWaitAddress());
// If the mutex is not yet acquired, acquire it.
mutex_val = Memory::Read32(thread->mutex_wait_address);
mutex_val = Memory::Read32(thread->GetMutexWaitAddress());
if (mutex_val != 0) {
monitor.ClearExclusive();
break;
}
} while (!monitor.ExclusiveWrite32(current_core, thread->mutex_wait_address,
thread->wait_handle));
} while (!monitor.ExclusiveWrite32(current_core, thread->GetMutexWaitAddress(),
thread->GetWaitHandle()));
if (mutex_val == 0) {
// We were able to acquire the mutex, resume this thread.
ASSERT(thread->status == ThreadStatus::WaitMutex);
ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex);
thread->ResumeFromWait();
auto lock_owner = thread->lock_owner;
if (lock_owner)
auto* const lock_owner = thread->GetLockOwner();
if (lock_owner != nullptr) {
lock_owner->RemoveMutexWaiter(thread);
}
thread->lock_owner = nullptr;
thread->mutex_wait_address = 0;
thread->condvar_wait_address = 0;
thread->wait_handle = 0;
thread->SetLockOwner(nullptr);
thread->SetMutexWaitAddress(0);
thread->SetCondVarWaitAddress(0);
thread->SetWaitHandle(0);
} else {
// Atomically signal that the mutex now has a waiting thread.
do {
monitor.SetExclusive(current_core, thread->mutex_wait_address);
monitor.SetExclusive(current_core, thread->GetMutexWaitAddress());
// Ensure that the mutex value is still what we expect.
u32 value = Memory::Read32(thread->mutex_wait_address);
u32 value = Memory::Read32(thread->GetMutexWaitAddress());
// TODO(Subv): When this happens, the kernel just clears the exclusive state and
// retries the initial read for this thread.
ASSERT_MSG(mutex_val == value, "Unhandled synchronization primitive case");
} while (!monitor.ExclusiveWrite32(current_core, thread->mutex_wait_address,
} while (!monitor.ExclusiveWrite32(current_core, thread->GetMutexWaitAddress(),
mutex_val | Mutex::MutexHasWaitersFlag));
// The mutex is already owned by some other thread, make this thread wait on it.
@@ -781,12 +822,12 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask);
auto owner = kernel.HandleTable().Get<Thread>(owner_handle);
ASSERT(owner);
ASSERT(thread->status == ThreadStatus::WaitMutex);
thread->wakeup_callback = nullptr;
ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex);
thread->InvalidateWakeupCallback();
owner->AddMutexWaiter(thread);
Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule();
Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
}
}
@@ -892,8 +933,8 @@ static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask)
return ERR_INVALID_HANDLE;
}
*core = thread->ideal_core;
*mask = thread->affinity_mask;
*core = thread->GetIdealCore();
*mask = thread->GetAffinityMask();
return RESULT_SUCCESS;
}
@@ -909,11 +950,13 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
}
if (core == static_cast<u32>(THREADPROCESSORID_DEFAULT)) {
ASSERT(thread->owner_process->ideal_processor !=
static_cast<u8>(THREADPROCESSORID_DEFAULT));
const u8 default_processor_id = thread->GetOwnerProcess()->GetDefaultProcessorID();
ASSERT(default_processor_id != static_cast<u8>(THREADPROCESSORID_DEFAULT));
// Set the target CPU to the one specified in the process' exheader.
core = thread->owner_process->ideal_processor;
mask = 1ull << core;
core = default_processor_id;
mask = 1ULL << core;
}
if (mask == 0) {
@@ -924,7 +967,7 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
static constexpr u32 OnlyChangeMask = static_cast<u32>(-3);
if (core == OnlyChangeMask) {
core = thread->ideal_core;
core = thread->GetIdealCore();
} else if (core >= Core::NUM_CPU_CORES && core != static_cast<u32>(-1)) {
return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidProcessorId);
}
@@ -1039,7 +1082,7 @@ static const FunctionDef SVC_Table[] = {
{0x2B, nullptr, "FlushDataCache"},
{0x2C, nullptr, "MapPhysicalMemory"},
{0x2D, nullptr, "UnmapPhysicalMemory"},
{0x2E, nullptr, "GetNextThreadInfo"},
{0x2E, nullptr, "GetFutureThreadInfo"},
{0x2F, nullptr, "GetLastThreadInfo"},
{0x30, nullptr, "GetResourceLimitLimitValue"},
{0x31, nullptr, "GetResourceLimitCurrentValue"},
@@ -1065,11 +1108,11 @@ static const FunctionDef SVC_Table[] = {
{0x45, nullptr, "CreateEvent"},
{0x46, nullptr, "Unknown"},
{0x47, nullptr, "Unknown"},
{0x48, nullptr, "AllocateUnsafeMemory"},
{0x49, nullptr, "FreeUnsafeMemory"},
{0x4A, nullptr, "SetUnsafeAllocationLimit"},
{0x4B, nullptr, "CreateJitMemory"},
{0x4C, nullptr, "MapJitMemory"},
{0x48, nullptr, "MapPhysicalMemoryUnsafe"},
{0x49, nullptr, "UnmapPhysicalMemoryUnsafe"},
{0x4A, nullptr, "SetUnsafeLimit"},
{0x4B, nullptr, "CreateCodeMemory"},
{0x4C, nullptr, "ControlCodeMemory"},
{0x4D, nullptr, "SleepSystem"},
{0x4E, nullptr, "ReadWriteRegister"},
{0x4F, nullptr, "SetProcessActivity"},
@@ -1104,7 +1147,7 @@ static const FunctionDef SVC_Table[] = {
{0x6C, nullptr, "SetHardwareBreakPoint"},
{0x6D, nullptr, "GetDebugThreadParam"},
{0x6E, nullptr, "Unknown"},
{0x6F, nullptr, "GetMemoryInfo"},
{0x6F, nullptr, "GetSystemInfo"},
{0x70, nullptr, "CreatePort"},
{0x71, nullptr, "ManageNamedPort"},
{0x72, nullptr, "ConnectToPort"},

View File

@@ -64,6 +64,11 @@ void SvcWrap() {
FuncReturn(func(Param(0), (s32)Param(1)).raw);
}
template <ResultCode func(u64, u32)>
void SvcWrap() {
FuncReturn(func(Param(0), static_cast<u32>(Param(1))).raw);
}
template <ResultCode func(u64*, u64)>
void SvcWrap() {
u64 param_1 = 0;

View File

@@ -65,15 +65,12 @@ void Thread::Stop() {
wait_objects.clear();
// Mark the TLS slot in the thread's page as free.
const u64 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE;
const u64 tls_slot =
((tls_address - Memory::TLS_AREA_VADDR) % Memory::PAGE_SIZE) / Memory::TLS_ENTRY_SIZE;
Core::CurrentProcess()->tls_slots[tls_page].reset(tls_slot);
owner_process->FreeTLSSlot(tls_address);
}
void WaitCurrentThread_Sleep() {
Thread* thread = GetCurrentThread();
thread->status = ThreadStatus::WaitSleep;
thread->SetStatus(ThreadStatus::WaitSleep);
}
void ExitCurrentThread() {
@@ -172,37 +169,11 @@ void Thread::ResumeFromWait() {
next_scheduler->ScheduleThread(this, current_priority);
// Change thread's scheduler
scheduler = next_scheduler;
scheduler = next_scheduler.get();
Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
}
/**
* Finds a free location for the TLS section of a thread.
* @param tls_slots The TLS page array of the thread's owner process.
* Returns a tuple of (page, slot, alloc_needed) where:
* page: The index of the first allocated TLS page that has free slots.
* slot: The index of the first free slot in the indicated page.
* alloc_needed: Whether there's a need to allocate a new TLS page (All pages are full).
*/
static std::tuple<std::size_t, std::size_t, bool> GetFreeThreadLocalSlot(
const std::vector<std::bitset<8>>& tls_slots) {
// Iterate over all the allocated pages, and try to find one where not all slots are used.
for (std::size_t page = 0; page < tls_slots.size(); ++page) {
const auto& page_tls_slots = tls_slots[page];
if (!page_tls_slots.all()) {
// We found a page with at least one free slot, find which slot it is
for (std::size_t slot = 0; slot < page_tls_slots.size(); ++slot) {
if (!page_tls_slots.test(slot)) {
return std::make_tuple(page, slot, false);
}
}
}
}
return std::make_tuple(0, 0, true);
}
/**
* Resets a thread context, making it ready to be scheduled and run by the CPU
* @param context Thread context to reset
@@ -223,7 +194,7 @@ static void ResetThreadContext(Core::ARM_Interface::ThreadContext& context, VAdd
ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name, VAddr entry_point,
u32 priority, u64 arg, s32 processor_id,
VAddr stack_top, SharedPtr<Process> owner_process) {
VAddr stack_top, Process& owner_process) {
// Check if priority is in ranged. Lowest priority -> highest priority id.
if (priority > THREADPRIO_LOWEST) {
LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority);
@@ -237,7 +208,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
// TODO(yuriks): Other checks, returning 0xD9001BEA
if (!Memory::IsValidVirtualAddress(*owner_process, entry_point)) {
if (!Memory::IsValidVirtualAddress(owner_process, entry_point)) {
LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:016X}", name, entry_point);
// TODO (bunnei): Find the correct error code to use here
return ResultCode(-1);
@@ -261,35 +232,10 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
thread->wait_handle = 0;
thread->name = std::move(name);
thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap();
thread->owner_process = owner_process;
thread->scheduler = Core::System::GetInstance().Scheduler(processor_id);
thread->owner_process = &owner_process;
thread->scheduler = Core::System::GetInstance().Scheduler(processor_id).get();
thread->scheduler->AddThread(thread, priority);
// Find the next available TLS index, and mark it as used
auto& tls_slots = owner_process->tls_slots;
auto [available_page, available_slot, needs_allocation] = GetFreeThreadLocalSlot(tls_slots);
if (needs_allocation) {
tls_slots.emplace_back(0); // The page is completely available at the start
available_page = tls_slots.size() - 1;
available_slot = 0; // Use the first slot in the new page
// Allocate some memory from the end of the linear heap for this region.
const std::size_t offset = thread->tls_memory->size();
thread->tls_memory->insert(thread->tls_memory->end(), Memory::PAGE_SIZE, 0);
auto& vm_manager = owner_process->vm_manager;
vm_manager.RefreshMemoryBlockMappings(thread->tls_memory.get());
vm_manager.MapMemoryBlock(Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE,
thread->tls_memory, 0, Memory::PAGE_SIZE,
MemoryState::ThreadLocal);
}
// Mark the slot as used
tls_slots[available_page].set(available_slot);
thread->tls_address = Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE +
available_slot * Memory::TLS_ENTRY_SIZE;
thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread);
// TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used
// to initialize the context
@@ -313,18 +259,19 @@ void Thread::BoostPriority(u32 priority) {
SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 priority,
Process& owner_process) {
// Setup page table so we can write to memory
SetCurrentPageTable(&owner_process.vm_manager.page_table);
SetCurrentPageTable(&owner_process.VMManager().page_table);
// Initialize new "main" thread
const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress();
auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, THREADPROCESSORID_0,
Memory::STACK_AREA_VADDR_END, &owner_process);
stack_top, owner_process);
SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
// Register 1 must be a handle to the main thread
thread->guest_handle = kernel.HandleTable().Create(thread).Unwrap();
thread->context.cpu_registers[1] = thread->guest_handle;
const Handle guest_handle = kernel.HandleTable().Create(thread).Unwrap();
thread->SetGuestHandle(guest_handle);
thread->GetContext().cpu_registers[1] = guest_handle;
// Threads by default are dormant, wake up the main thread so it runs when the scheduler fires
thread->ResumeFromWait();
@@ -352,6 +299,18 @@ VAddr Thread::GetCommandBufferAddress() const {
return GetTLSAddress() + CommandHeaderOffset;
}
void Thread::SetStatus(ThreadStatus new_status) {
if (new_status == status) {
return;
}
if (status == ThreadStatus::Running) {
last_running_ticks = CoreTiming::GetTicks();
}
status = new_status;
}
void Thread::AddMutexWaiter(SharedPtr<Thread> thread) {
if (thread->lock_owner == this) {
// If the thread is already waiting for this thread to release the mutex, ensure that the
@@ -441,11 +400,23 @@ void Thread::ChangeCore(u32 core, u64 mask) {
next_scheduler->ScheduleThread(this, current_priority);
// Change thread's scheduler
scheduler = next_scheduler;
scheduler = next_scheduler.get();
Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
}
bool Thread::AllWaitObjectsReady() {
return std::none_of(
wait_objects.begin(), wait_objects.end(),
[this](const SharedPtr<WaitObject>& object) { return object->ShouldWait(this); });
}
bool Thread::InvokeWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> thread,
SharedPtr<WaitObject> object, std::size_t index) {
ASSERT(wakeup_callback);
return wakeup_callback(reason, std::move(thread), std::move(object), index);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/**

View File

@@ -62,6 +62,18 @@ enum class ThreadWakeupReason {
class Thread final : public WaitObject {
public:
using TLSMemory = std::vector<u8>;
using TLSMemoryPtr = std::shared_ptr<TLSMemory>;
using MutexWaitingThreads = std::vector<SharedPtr<Thread>>;
using ThreadContext = Core::ARM_Interface::ThreadContext;
using ThreadWaitObjects = std::vector<SharedPtr<WaitObject>>;
using WakeupCallback = std::function<bool(ThreadWakeupReason reason, SharedPtr<Thread> thread,
SharedPtr<WaitObject> object, std::size_t index)>;
/**
* Creates and returns a new thread. The new thread is immediately scheduled
* @param kernel The kernel instance this thread will be created under.
@@ -77,7 +89,7 @@ public:
static ResultVal<SharedPtr<Thread>> Create(KernelCore& kernel, std::string name,
VAddr entry_point, u32 priority, u64 arg,
s32 processor_id, VAddr stack_top,
SharedPtr<Process> owner_process);
Process& owner_process);
std::string GetName() const override {
return name;
@@ -102,6 +114,14 @@ public:
return current_priority;
}
/**
* Gets the thread's nominal priority.
* @return The current thread's nominal priority.
*/
u32 GetNominalPriority() const {
return nominal_priority;
}
/**
* Sets the thread's current priority
* @param priority The new priority
@@ -130,10 +150,18 @@ public:
* Gets the thread's thread ID
* @return The thread's ID
*/
u32 GetThreadId() const {
u32 GetThreadID() const {
return thread_id;
}
TLSMemoryPtr& GetTLSMemory() {
return tls_memory;
}
const TLSMemoryPtr& GetTLSMemory() const {
return tls_memory;
}
/**
* Resumes a thread from waiting
*/
@@ -192,6 +220,11 @@ public:
return tpidr_el0;
}
/// Sets the value of the TPIDR_EL0 Read/Write system register for this thread.
void SetTPIDR_EL0(u64 value) {
tpidr_el0 = value;
}
/*
* Returns the address of the current thread's command buffer, located in the TLS.
* @returns VAddr of the thread's command buffer.
@@ -207,69 +240,193 @@ public:
return status == ThreadStatus::WaitSynchAll;
}
Core::ARM_Interface::ThreadContext context;
ThreadContext& GetContext() {
return context;
}
u32 thread_id;
const ThreadContext& GetContext() const {
return context;
}
ThreadStatus status;
VAddr entry_point;
VAddr stack_top;
ThreadStatus GetStatus() const {
return status;
}
u32 nominal_priority; ///< Nominal thread priority, as set by the emulated application
u32 current_priority; ///< Current thread priority, can be temporarily changed
void SetStatus(ThreadStatus new_status);
u64 last_running_ticks; ///< CPU tick when thread was last running
u64 GetLastRunningTicks() const {
return last_running_ticks;
}
s32 processor_id;
s32 GetProcessorID() const {
return processor_id;
}
VAddr tls_address; ///< Virtual address of the Thread Local Storage of the thread
u64 tpidr_el0; ///< TPIDR_EL0 read/write system register.
Process* GetOwnerProcess() {
return owner_process;
}
SharedPtr<Process> owner_process; ///< Process that owns this thread
const Process* GetOwnerProcess() const {
return owner_process;
}
/// Objects that the thread is waiting on, in the same order as they were
// passed to WaitSynchronization1/N.
std::vector<SharedPtr<WaitObject>> wait_objects;
const ThreadWaitObjects& GetWaitObjects() const {
return wait_objects;
}
/// List of threads that are waiting for a mutex that is held by this thread.
std::vector<SharedPtr<Thread>> wait_mutex_threads;
void SetWaitObjects(ThreadWaitObjects objects) {
wait_objects = std::move(objects);
}
/// Thread that owns the lock that this thread is waiting for.
SharedPtr<Thread> lock_owner;
void ClearWaitObjects() {
wait_objects.clear();
}
// If waiting on a ConditionVariable, this is the ConditionVariable address
VAddr condvar_wait_address;
VAddr mutex_wait_address; ///< If waiting on a Mutex, this is the mutex address
Handle wait_handle; ///< The handle used to wait for the mutex.
/// Determines whether all the objects this thread is waiting on are ready.
bool AllWaitObjectsReady();
// If waiting for an AddressArbiter, this is the address being waited on.
VAddr arb_wait_address{0};
const MutexWaitingThreads& GetMutexWaitingThreads() const {
return wait_mutex_threads;
}
std::string name;
Thread* GetLockOwner() const {
return lock_owner.get();
}
/// Handle used by guest emulated application to access this thread
Handle guest_handle;
void SetLockOwner(SharedPtr<Thread> owner) {
lock_owner = std::move(owner);
}
/// Handle used as userdata to reference this object when inserting into the CoreTiming queue.
Handle callback_handle;
VAddr GetCondVarWaitAddress() const {
return condvar_wait_address;
}
using WakeupCallback = bool(ThreadWakeupReason reason, SharedPtr<Thread> thread,
SharedPtr<WaitObject> object, std::size_t index);
// Callback that will be invoked when the thread is resumed from a waiting state. If the thread
// was waiting via WaitSynchronizationN then the object will be the last object that became
// available. In case of a timeout, the object will be nullptr.
std::function<WakeupCallback> wakeup_callback;
void SetCondVarWaitAddress(VAddr address) {
condvar_wait_address = address;
}
std::shared_ptr<Scheduler> scheduler;
VAddr GetMutexWaitAddress() const {
return mutex_wait_address;
}
u32 ideal_core{0xFFFFFFFF};
u64 affinity_mask{0x1};
void SetMutexWaitAddress(VAddr address) {
mutex_wait_address = address;
}
Handle GetWaitHandle() const {
return wait_handle;
}
void SetWaitHandle(Handle handle) {
wait_handle = handle;
}
VAddr GetArbiterWaitAddress() const {
return arb_wait_address;
}
void SetArbiterWaitAddress(VAddr address) {
arb_wait_address = address;
}
void SetGuestHandle(Handle handle) {
guest_handle = handle;
}
bool HasWakeupCallback() const {
return wakeup_callback != nullptr;
}
void SetWakeupCallback(WakeupCallback callback) {
wakeup_callback = std::move(callback);
}
void InvalidateWakeupCallback() {
SetWakeupCallback(nullptr);
}
/**
* Invokes the thread's wakeup callback.
*
* @pre A valid wakeup callback has been set. Violating this precondition
* will cause an assertion to trigger.
*/
bool InvokeWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> thread,
SharedPtr<WaitObject> object, std::size_t index);
u32 GetIdealCore() const {
return ideal_core;
}
u64 GetAffinityMask() const {
return affinity_mask;
}
private:
explicit Thread(KernelCore& kernel);
~Thread() override;
std::shared_ptr<std::vector<u8>> tls_memory = std::make_shared<std::vector<u8>>();
Core::ARM_Interface::ThreadContext context{};
u32 thread_id = 0;
ThreadStatus status = ThreadStatus::Dormant;
VAddr entry_point = 0;
VAddr stack_top = 0;
u32 nominal_priority = 0; ///< Nominal thread priority, as set by the emulated application
u32 current_priority = 0; ///< Current thread priority, can be temporarily changed
u64 last_running_ticks = 0; ///< CPU tick when thread was last running
s32 processor_id = 0;
VAddr tls_address = 0; ///< Virtual address of the Thread Local Storage of the thread
u64 tpidr_el0 = 0; ///< TPIDR_EL0 read/write system register.
/// Process that owns this thread
Process* owner_process;
/// Objects that the thread is waiting on, in the same order as they were
/// passed to WaitSynchronization1/N.
ThreadWaitObjects wait_objects;
/// List of threads that are waiting for a mutex that is held by this thread.
MutexWaitingThreads wait_mutex_threads;
/// Thread that owns the lock that this thread is waiting for.
SharedPtr<Thread> lock_owner;
/// If waiting on a ConditionVariable, this is the ConditionVariable address
VAddr condvar_wait_address = 0;
/// If waiting on a Mutex, this is the mutex address
VAddr mutex_wait_address = 0;
/// The handle used to wait for the mutex.
Handle wait_handle = 0;
/// If waiting for an AddressArbiter, this is the address being waited on.
VAddr arb_wait_address{0};
/// Handle used by guest emulated application to access this thread
Handle guest_handle = 0;
/// Handle used as userdata to reference this object when inserting into the CoreTiming queue.
Handle callback_handle = 0;
/// Callback that will be invoked when the thread is resumed from a waiting state. If the thread
/// was waiting via WaitSynchronizationN then the object will be the last object that became
/// available. In case of a timeout, the object will be nullptr.
WakeupCallback wakeup_callback;
Scheduler* scheduler = nullptr;
u32 ideal_core{0xFFFFFFFF};
u64 affinity_mask{0x1};
TLSMemoryPtr tls_memory = std::make_shared<TLSMemory>();
std::string name;
};
/**

Some files were not shown because too many files have changed in this diff Show More