Compare commits

...

482 Commits

Author SHA1 Message Date
Lioncash
9046f764bf rasterizer_cache: Remove reliance on the System singleton
Rather than have a transparent dependency, we can make it explicit in
the interface. This also gets rid of the need to put the core include in
a header.
2018-11-08 06:16:38 -05:00
Lioncash
29f082775b gl_resource_manager: Amend clang-format discrepancies
Fixes the buildbot.
2018-11-08 00:23:45 -05:00
bunnei
0e05a9d58f Merge pull request #1658 from ogniK5377/holdtype-style
Updated npad styles on holdtype switches
2018-11-07 20:59:01 -08:00
David
581406af18 svcBreak now dumps information from the debug buffer passed (#1646)
* svcBreak now dumps information from the debug buffer passed

info1 and info2 seem to somtimes hold an address to a buffer, this is usually 4 bytes or the size of the int and contains an error code. There's other circumstances where it can be something different so we hexdump these to examine them at a later date.

* Addressed comments
2018-11-07 20:43:54 -08:00
bunnei
481d8716e0 Merge pull request #1655 from ogniK5377/shantae
Implement acc:TrySelectUserWithoutInteraction
2018-11-07 20:41:06 -08:00
David Marcec
a9c25ab9e4 Updated npad styles on holdtype switches
Fixes input for megaman
2018-11-08 01:07:14 +11:00
bunnei
81ff9e2473 Merge pull request #1630 from bunnei/fix-mapbufferex
memory_manager: Do not MapBufferEx over already in use memory.
2018-11-07 00:14:36 -08:00
bunnei
74bce4d68f Merge pull request #1635 from Tinob/master
Implement multi-target viewports and blending
2018-11-07 00:11:49 -08:00
bunnei
e5a0a23553 Merge pull request #1653 from degasus/profiler
gl_rasterizer: Update microprofile scopes.
2018-11-07 00:10:13 -08:00
David Marcec
ad45d68871 fixed spelling error 2018-11-07 12:04:43 +11:00
David Marcec
49cb4fa37b Added missing log 2018-11-07 11:46:04 +11:00
David Marcec
92fcc6d15a Implement acc:TrySelectUserWithoutInteraction
Needed for Shantae - Half-Genie Hero - Ultimate Edition!
2018-11-07 11:45:01 +11:00
Markus Wick
2c87f10267 gl_rasterizer_cache: Add profiles for Copy and Blit.
They were missed, and Copy is very high in profile here. It doesn't block the GPU,
but it stalls the driver thread. So with our bad GL instructions, this might block quite a while.
2018-11-06 17:45:32 +01:00
Markus Wick
7e59e907ef gl_resource_manager: Profile creation and deletion. 2018-11-06 17:45:32 +01:00
Markus Wick
80e4dbdce7 gl_stream_buffer: Profile orphaning of stream buffer.
This serialize to the driver thread and so it may block for a while.
So if it is in the benchmark, we get noticed if it happens too often.
2018-11-06 17:45:32 +01:00
Markus Wick
2ba4d878e5 microprofile: Drop ReleaseActiveBuffer scope.
This was created with the unfinished resampling PR in mind.
As the resampling is now on the audio thread, we don't need to care about this here any more.
2018-11-06 17:45:32 +01:00
bunnei
dd321dc85f Merge pull request #1649 from degasus/split_resource_manager
gl_resource_manager: Split implementations in .cpp file.
2018-11-06 11:43:17 -05:00
Markus Wick
54df9fe29e gl_resource_manager: Split implementations in .cpp file.
Those implementations are quite costly, so there is no need to inline them to the caller.
Ressource deletion is often a performance bug, so in this way, we support to add breakpoints to them.
2018-11-06 14:40:39 +01:00
bunnei
cdb19e71fe Merge pull request #1616 from FernandoS27/cube-array
Implement Cube Arrays
2018-11-05 15:28:48 -05:00
bunnei
722e7c05de Merge pull request #1633 from ogniK5377/reload-input
Fixed HID crash when launching more than 1 game & signaled styleset change event
2018-11-05 00:21:27 -05:00
bunnei
e10483a878 Merge pull request #1441 from CarlKenner/DebuggerLog
logging: Add DebuggerBackend for logging to Visual Studio
2018-11-05 00:19:59 -05:00
bunnei
acdc770cfb Merge pull request #1639 from DarkLordZach/open-yuzu-folder
qt: Add help option to open yuzu folder
2018-11-05 00:19:15 -05:00
Rodolfo Bogado
19038db489 Add support to color mask to avoid issues in blending caused by wrong values in the alpha channel in some render targets. 2018-11-05 00:24:19 -03:00
Rodolfo Bogado
145ae36963 Implement multi-target viewports and blending 2018-11-04 20:49:48 -03:00
bunnei
38c1c500ab Merge pull request #1625 from FernandoS27/astc
Implement ASTC Textures 5x5 and fix a bunch of ASTC texture problems
2018-11-04 18:47:26 -05:00
Mat M
d46e0acb3c Merge pull request #1645 from dharmin/master
Fix quickstart link
2018-11-04 18:05:33 -05:00
Mat M
e450b0bbac Merge pull request #1643 from FreddyFunk/typo
Fix typo in BufferTransformFlags
2018-11-04 18:04:50 -05:00
Dharmin K Shah
b2647dba33 Fix quickstart link 2018-11-05 00:36:49 +05:30
Frederic Laing
1c4365d928 Fix typo in BufferTransformFlags 2018-11-04 16:56:10 +01:00
Zach Hilman
97187b7ef6 qt: Add help option to open yuzu folder
Opens a new file manager window at the UserDir.
2018-11-03 12:48:34 -04:00
bunnei
6664d7b2c5 Merge pull request #1636 from ogniK5377/hwopus-bad-assert
Fixed incorrect hwopus assert
2018-11-03 00:29:15 -04:00
David Marcec
03c26d3406 Fixed incorrect hwopus assert 2018-11-02 15:23:38 +11:00
David Marcec
0bc323bafb Fixed HID crash when launching more than 1 game & signaled syleset change event
This should fix crashes when launching multiple games in yuzu
2018-11-02 12:35:49 +11:00
FernandoS27
60a184455c Fix ASTC Decompressor to support depth parameter 2018-11-01 19:22:12 -04:00
bunnei
1069eced84 Merge pull request #1615 from lioncash/input
configure_system: Contrain profile usernames to 32 characters
2018-11-01 19:10:26 -04:00
bunnei
4aa9779ae1 memory_manager: Do not MapBufferEx over already in use memory.
- This fixes rendering when changing areas in Super Mario Odyssey.
2018-11-01 18:57:59 -04:00
bunnei
cc1fe93297 Merge pull request #1623 from Tinob/master
Improve OpenGL state handling
2018-11-01 15:53:33 -04:00
FernandoS27
aee93f98f9 Fix ASTC formats 2018-11-01 13:08:19 -04:00
FernandoS27
31930a3334 Implemented ASTC 5x5 2018-11-01 13:06:24 -04:00
FernandoS27
678c18aa5c Implement Cube Arrays 2018-11-01 11:56:19 -04:00
bunnei
9afcbba8e4 Merge pull request #1527 from FernandoS27/assert-flow
Assert Control Flow Instructions using Control Codes
2018-11-01 00:34:56 -04:00
bunnei
7992dee8e9 Merge pull request #1622 from bunnei/fix-macros
maxwell_3d: Restructure macro upload to use a single macro code memory.
2018-11-01 00:14:59 -04:00
bunnei
de0ab806df maxwell_3d: Restructure macro upload to use a single macro code memory.
- Fixes an issue where macros could be skipped.
- Fixes rendering of distant objects in Super Mario Odyssey.
2018-10-31 23:29:21 -04:00
bunnei
d08457f879 Merge pull request #1604 from FearlessTobi/port-4369
Port citra-emu/citra#4369: "compatdb: Use a seperate endpoint for testcase submission"
2018-10-31 22:37:07 -04:00
bunnei
86e70cf302 Merge pull request #1528 from FernandoS27/assert-control-codes
Assert Control Codes Generation on Shader Instructions
2018-10-31 22:34:18 -04:00
bunnei
0b33d38e9b Merge pull request #1614 from ReinUsesLisp/surface-params
video_core: Move surface declarations out of gl_rasterizer_cache
2018-10-31 22:31:02 -04:00
bunnei
e7fc3d13ed Merge pull request #1626 from lioncash/table
service/usb: Update IPdSession's function table
2018-10-31 22:28:58 -04:00
Lioncash
a6830e61b8 configure_system: Contrain profile usernames to 32 characters
Previously, we would let a user enter an unbounded name and then
silently truncate away characters that went over the 32-character limit.
This is kind of bad from the UX point of view, because we're essentially
not doing what the user intended in certain scenarios.

Instead, we clamp it to 32 characters and make that visually apparent in
the dialog box to provide a name for a user.
2018-10-31 02:05:00 -04:00
Rodolfo Bogado
aca218aea0 Improve OpenGL state handling 2018-10-30 21:19:04 -03:00
Lioncash
9b9c586dff service/usb: Update IPdSession's function table
Updated based off information on SwitchBrew.
2018-10-30 15:23:49 -04:00
ReinUsesLisp
76754f5705 video_core: Move surface declarations out of gl_rasterizer_cache 2018-10-30 16:07:20 -03:00
FernandoS27
5bb80ab009 Assert Control Codes Generation 2018-10-30 13:37:55 -04:00
bunnei
da5fcbf501 Merge pull request #1624 from lioncash/boost
general: Remove unused boost inclusions where applicable
2018-10-30 13:13:39 -04:00
bunnei
c31412c433 Merge pull request #1595 from FreddyFunk/cast
configure_system: Fix compiler warning
2018-10-30 00:27:41 -04:00
Lioncash
352b56367c general: Remove unused boost inclusions where applicable
Cleans up unused includes and trims off some dependencies on externals.
2018-10-30 00:09:46 -04:00
Frederic L
7a5eda5914 global: Use std::optional instead of boost::optional (#1578)
* get rid of boost::optional

* Remove optional references

* Use std::reference_wrapper for optional references

* Fix clang format

* Fix clang format part 2

* Adressed feedback

* Fix clang format and MacOS build
2018-10-30 00:03:25 -04:00
bunnei
adf26ae668 Merge pull request #1621 from lioncash/ipc
hle_ipc: Make GetDomainMessageHeader return a regular pointer
2018-10-29 23:55:59 -04:00
Lioncash
6383653a8d hle_ipc: Add member function for querying the existence of a domain header
Gets rid of the need to call the getter and then check for null.
2018-10-29 23:28:04 -04:00
Lioncash
0cc347462d hle_ipc: Make GetDomainMessageHeader return a regular pointer
Nothing requires the shared owner ship here, so we can just return a
plain pointer.
2018-10-29 23:18:25 -04:00
bunnei
938e45eb83 Merge pull request #1611 from lioncash/const
core: Add missing const variants of getters for the System class
2018-10-29 22:48:59 -04:00
bunnei
c5a849212f Merge pull request #1580 from FernandoS27/mm-impl
Implemented Mipmaps
2018-10-29 22:34:00 -04:00
bunnei
f1cb425d92 Merge pull request #1617 from FearlessTobi/fix-stretch-delay
time_stretch: Switch to values of Citra
2018-10-29 19:09:30 -04:00
bunnei
0270906dbf Merge pull request #1613 from ReinUsesLisp/gl-utils
video_core: Move OpenGL specific utils to its renderer
2018-10-29 13:22:14 -04:00
fearlessTobi
655694253a time_stretch: Switch to values of Citra 2018-10-29 14:49:36 +01:00
bunnei
5d7167dfca Merge pull request #1610 from slashiee/dxt1-alpha
renderer_opengl: Enable alpha channel for DXT1 texture format
2018-10-28 21:29:43 -04:00
bunnei
9049aedd83 Merge pull request #1612 from Tinob/master
renderer_opengl: Correct bpp value for ASTC_2D_8X5_SRGB
2018-10-28 21:28:34 -04:00
ReinUsesLisp
80cbd81276 video_core: Move OpenGL specific utils to its renderer 2018-10-28 22:22:30 -03:00
Rodolfo Bogado
e8b565b239 renderer_opengl: Correct bpp value for ASTC_2D_8X5_SRGB 2018-10-28 20:52:57 -03:00
FernandoS27
3aa8b644a9 Assert Control Flow Instructions using Control Codes 2018-10-28 19:16:41 -04:00
FernandoS27
dde3094058 Fixed black textures, pixelation and we no longer require to auto-generate mipmaps 2018-10-28 19:00:49 -04:00
FernandoS27
f0e902a7d6 Fixed mipmap block autosizing algorithm 2018-10-28 19:00:05 -04:00
FernandoS27
87f8181405 Fixed Invalid Image size and Mipmap calculation 2018-10-28 19:00:04 -04:00
FernandoS27
f4432b5d0c Fixed Block Resizing algorithm and Clang Format 2018-10-28 19:00:03 -04:00
FernandoS27
258f0f5c31 Implement Mip Filter 2018-10-28 19:00:01 -04:00
FernandoS27
dc85e3bff1 Zero out memory region of recreated surface before flushing 2018-10-28 19:00:00 -04:00
FernandoS27
bbf3b2da0c Implement Mipmaps 2018-10-28 18:59:59 -04:00
Lioncash
a973a049b7 core: Make System references const where applicable 2018-10-28 17:45:29 -04:00
Lioncash
b77f571d20 core: Add missing const variants of getters for the System class
Many of the Current<Thing> getters (as well as a few others) were
missing const qualified variants, which makes it a pain to retrieve
certain things from const qualified references to System.
2018-10-28 17:44:58 -04:00
Michael
635d1e5651 Enable alpha channel for DXT1 texture format 2018-10-28 14:11:04 -07:00
bunnei
b5f8a5f0a3 Merge pull request #1607 from FearlessTobi/patch-3
renderer_opengl: Correct bpp value for ASTC_2D_8X5
2018-10-28 16:59:13 -04:00
Frederic Laing
f50f065c31 configure_system: Fix compiler warning 2018-10-28 21:13:09 +01:00
Tobias
351d5a2227 Correct bpp value for ASTC_2D_8X5 2018-10-28 19:49:10 +01:00
bunnei
aa1cf608ed Merge pull request #1601 from FernandoS27/shader-precision
Improved Shader accuracy on Vertex and Geometry Shaders.
2018-10-28 13:06:21 -04:00
bunnei
4ddbd9bbaf Merge pull request #1606 from FearlessTobi/revert-1581-macosx-target-version
Revert "Update MACOSX_DEPLOYMENT_TARGET to 10.14"
2018-10-28 12:11:00 -04:00
FernandoS27
e5ca097e32 Refactor precise usage and add FMNMX, MUFU, FMUL32 and FADD332 2018-10-28 11:38:40 -04:00
Tobias
03150a560e Revert "Update MACOSX_DEPLOYMENT_TARGET to 10.14" 2018-10-28 13:43:42 +01:00
fearlessTobi
585b6a6a50 compatdb: Use a seperate endpoint for testcase submission 2018-10-28 13:23:02 +01:00
bunnei
2239d47112 Merge pull request #1593 from lioncash/svc
svc: Implement svcGetInfo command 0xF0000002
2018-10-28 04:38:48 -04:00
bunnei
b32be35173 Merge pull request #1581 from FreddyFunk/macosx-target-version
Update MACOSX_DEPLOYMENT_TARGET to 10.14
2018-10-28 04:32:11 -04:00
Frederic L
72d10ce66c file_sys/patch_manager: Remove unnecessary if-statements (#1586)
* remove unnecessary if-statements

* Addressed feedback
2018-10-28 02:30:29 -04:00
bunnei
0d449b77e2 Merge pull request #1598 from DeeJayBro/delete-directory
service/filesystem: Implemented DeleteDirectory & DeleteDirectoryRecursive
2018-10-28 02:29:52 -04:00
bunnei
6f620b2441 Merge pull request #1600 from DarkLordZach/nsp-secondary-loader-fix
loader/nsp: Move secondary loader initialization to constructor
2018-10-28 02:29:14 -04:00
bunnei
1fca683388 Merge pull request #1582 from Tinob/master
Implement sRGB support
2018-10-28 00:24:36 -04:00
Rodolfo Bogado
0287b2be6d Implement sRGB Support, including workarounds for nvidia driver issues and QT sRGB support 2018-10-28 01:13:55 -03:00
bunnei
a9d60c6103 Merge pull request #1602 from DarkLordZach/key-derivation-isxdigit
key_manager: Use isxdigit instead of isdigit when reading key file
2018-10-27 23:54:48 -04:00
Zach Hilman
1fa31cf74d key_manager: Use isxdigit instead of isdigit when reading key file
Crypto revisions are hex numbers and this function only checks if the string is valid for stoul in base 16, so it should be isxdigit.
2018-10-27 21:56:10 -04:00
bunnei
8d89e88d2c Merge pull request #1597 from lioncash/error
configure_system: Indicate when filesystem operations fail
2018-10-27 21:09:31 -04:00
bunnei
d63f5acb15 Merge pull request #1594 from FreddyFunk/static-cast
gl_rasterizer_cache: Fix compiler warning
2018-10-27 21:09:06 -04:00
FernandoS27
d8d557df86 Improved Shader accuracy on Vertex and Geometry Shaders with FFMA, FMUL and FADD 2018-10-27 20:09:26 -04:00
bunnei
a9dc34ea5c Merge pull request #1596 from FearlessTobi/port-4367
Port citra-emu/citra#4367: "cubeb_sink: ignore null-name device when selecting"
2018-10-27 13:26:24 -04:00
bunnei
ed95ce6bb7 Merge pull request #1592 from bunnei/prim-restart
gl_rasterizer: Implement primitive restart.
2018-10-27 13:25:00 -04:00
bunnei
ac8231ed10 Merge pull request #1599 from FernandoS27/stalemate
Implement Default Block Height for each format
2018-10-27 12:05:14 -04:00
FernandoS27
705300992e Implement Default Block Height for each format 2018-10-27 10:17:39 -04:00
Zach Hilman
7c70746ec4 loader/nsp: Move secondary loader initialization to constructor
Prevents nullptr bug when trying to dump the RomFS of an NSP resulting from secondary_loader not being initialized.
2018-10-27 10:16:29 -04:00
Frederic Laing
0bf24d310e gl_rasterizer_cache: Fix compiler warning 2018-10-27 13:06:26 +02:00
DeeJayBro
3b1e4c0995 service/filesystem: Add DirectoryDelete & DirectoryDeleteRecursively 2018-10-27 11:56:39 +02:00
Lioncash
9024cbb5b8 configure_system: Make GetIcon() return the scaled 64x64 icon
Avoids the need to put the scaling parameters all over the place for the
common case. The only other time scaling is done is to generate the
smaller 48x48 image, so this is fine.
2018-10-27 01:05:56 -04:00
Lioncash
85ed0af84e configure_system: Move entry formatting for the user account list entries to its own function
Avoids the need to duplicate this all over the place, and makes it
translator-friendly across the board.
2018-10-27 01:05:56 -04:00
Lioncash
8eaf857d06 configure_system: Display errors to the user if file operations fail when setting user images
We should display an error to the user if setting a user image for an
account fails, rather than continuing onwards.
2018-10-27 01:05:50 -04:00
Weiyi Wang
12c365b549 cubeb_sink: ignore null-name device when selecting
We already ignore them on listing devices. We should do the same when selecting devices. This fix a crash when opening a specific device while there is a null device in the list
2018-10-27 00:43:04 +02:00
Lioncash
7de8e36343 svc: Localize the GetInfo enum class to the function itself
Nothing from this enum is intended to be used outside of this function.
2018-10-26 12:49:14 -04:00
Lioncash
6594853eb1 svc: Implement svcGetInfo command 0xF0000002
This retrieves:

if (curr_thread == handle_thread) {
   result = total_thread_ticks + (hardware_tick_count - last_context_switch_ticks);
} else if (curr_thread == handle_thread && sub_id == current_core_index) {
   result = hardware_tick_count - last_context_switch_ticks;
}
2018-10-26 12:49:11 -04:00
bunnei
58444a0376 gl_rasterizer: Implement primitive restart. 2018-10-26 00:42:57 -04:00
bunnei
d278f25bda Merge pull request #1533 from FernandoS27/lmem
Implemented Shader Local Memory
2018-10-26 00:16:25 -04:00
bunnei
72e6b31a07 Merge pull request #1430 from DarkLordZach/remove-promote-dir
vfs: Remove InterpretAsDirectory and related functions
2018-10-26 00:15:34 -04:00
bunnei
1f98dc30ea Merge pull request #1591 from bunnei/depth-range
gl_rasterizer: Implement depth range.
2018-10-26 00:12:20 -04:00
bunnei
949d9a7136 maxwell_3d: Add code for initializing register defaults. 2018-10-25 23:42:39 -04:00
bunnei
debabf1fa6 Merge pull request #1569 from lioncash/amiibo
yuzu/main: Notify user of loading errors with Amiibo data
2018-10-25 22:10:08 -04:00
bunnei
476b9f8fc5 Merge pull request #1587 from lioncash/private
configure_system: Minor cleanup-related changes
2018-10-25 22:08:06 -04:00
bunnei
a94831f2a9 Merge pull request #1557 from bunnei/ldr_ro
Implement the LoadNro functions from the ldr:ro service.
2018-10-25 21:54:35 -04:00
bunnei
8cea598158 gl_rasterizer: Implement depth range. 2018-10-25 21:53:24 -04:00
bunnei
cebce2a93a ldr: Partially implement LoadNro.
- This is an incomplete implementation. It was tested with Super Mario Party.
2018-10-25 18:03:54 -04:00
bunnei
c2049aa4e5 process: LoadModule should clear JIT instruction cache. 2018-10-25 18:03:54 -04:00
bunnei
a609b6907a Kernel/Memory: Added a function to first a suitable guest address at which to allocate a region of a given size. 2018-10-25 18:03:54 -04:00
bunnei
ef7b2237d9 nro: Make LoadNro method accessible outside of apploader code. 2018-10-25 18:03:54 -04:00
Mat M
a141b46b5c Merge pull request #1583 from DarkLordZach/rle-size
ips_layer: Use rle_size instead of data_size in RLE patch application
2018-10-25 17:54:38 -04:00
Lioncash
85285b09b0 configure_system: Make the file selector text translatable
This should be localizable, since it's user-facing text.
2018-10-25 17:27:30 -04:00
Lioncash
5172354e29 configure_system: Make GetAccountUsername() an internal function
We can just make the function accept an arbitrary ProfileManager
reference and operate on that instead of tying the function to the class
itself. This allows us to keep the function internal to the cpp file and
removes the need to forward declare the UUID struct.
2018-10-25 17:27:25 -04:00
Lioncash
bf7da804c5 configure_system: Default initialize member variables
These should be initialized to deterministic values so it's easier to
catch improper behavior, as it'll always be reproducable, instead of
performing uninitialized reads.
2018-10-25 16:52:23 -04:00
Lioncash
8806e69f59 configure_system: Simplify UUID generation call in AddUser()
This is a static function so we can just perform an assignment directly.
2018-10-25 16:50:07 -04:00
Lioncash
a6addb5332 configure_system: Amend function casing 2018-10-25 16:47:09 -04:00
Lioncash
2347e1b8c5 configure_system: Add missing override specifier on the destructor 2018-10-25 16:45:13 -04:00
Lioncash
3c63cecb96 configure_system: Make public slots private
These are only used within this class, so we can make them private to
keep their use contained. This also gets rid of the pre-Qt5 'slot'
identifier, since Qt 5's connection syntax doesn't require a function to
be declared a slot anymore.
2018-10-25 16:43:44 -04:00
James Rowe
e54c9e19f3 Merge pull request #1584 from FearlessTobi/patch-3
Delete git file
2018-10-25 13:55:10 -06:00
Tobias
bf0d3e1fea Delete git 2018-10-25 21:53:41 +02:00
Zach Hilman
9a87ece837 ips_layer: Use rle_size instead of data_size in RLE patch application
Prevents a potential bug when using RLE records in an IPS patch.
2018-10-25 14:23:56 -04:00
bunnei
2bc2b32662 Merge pull request #1579 from lioncash/usb
service/usb: Update service function tables
2018-10-25 12:22:57 -04:00
bunnei
0634aee267 Merge pull request #1576 from lioncash/acc-warn
service/acc: Silence compiler truncation warnings
2018-10-25 12:22:10 -04:00
Frederic L
942def7831 Update MACOSX_DEPLOYMENT_TARGET to 10.14 2018-10-25 08:35:42 +02:00
bunnei
c94db0e071 Merge pull request #1577 from lioncash/err
kernel/error: Amend error return code values
2018-10-25 01:10:26 -04:00
bunnei
f7a173de6c Merge pull request #1524 from FernandoS27/layers-fix
rasterizer: Fix Layered Textures Loading and Cubemaps
2018-10-25 00:29:18 -04:00
bunnei
b43cfe6c02 Merge pull request #1575 from lioncash/qstring
game_list_worker: Use QString's formatting instead of fmt in FormatPatchNameVersions()
2018-10-24 19:39:43 -04:00
Lioncash
fbbb58b226 service/usb: Update service function tables
Updated based off the information provided by Hexkyz on Switchbrew.
2018-10-24 19:07:55 -04:00
Lioncash
1e3b139cd7 service/acc: Move fallback image to file scope
This is just flat data, so it doesn't really need to be in the function
itself. This also allows deduplicating the constant for the backup size
in GetImageSize().
2018-10-24 18:22:24 -04:00
Lioncash
6f00628564 service/acc: Silence compiler warnings
Silences compiler warnings related to truncation. This also introduces a
small helper function to perform the clamping of the image size.
2018-10-24 18:22:24 -04:00
Lioncash
3ec90dc6ef service/acc: Early return in failure case in LoadImage()
Allows unindenting the other branch's code.
2018-10-24 18:22:20 -04:00
bunnei
9aa5c1894e Merge pull request #1570 from lioncash/optional
profile_manager: Use std::optional instead of boost::optional
2018-10-24 18:11:03 -04:00
bunnei
3a6e76e9b5 Merge pull request #1558 from lioncash/ptr
yuzu/configuration/config: Use a std::unique_ptr for qt_config instead of a raw pointer
2018-10-24 18:07:14 -04:00
FernandoS27
ca142f35c0 Implemented LD_L and ST_L 2018-10-24 17:51:53 -04:00
FernandoS27
abefe29398 Implement Shader Local Memory 2018-10-24 17:50:43 -04:00
bunnei
29f748a658 Merge pull request #1565 from lioncash/audio
time_stretch: Remove unused m_channel_count member variable
2018-10-24 17:39:53 -04:00
bunnei
69b35d7615 Merge pull request #1554 from FernandoS27/pointsize
Implement PointSize Output Attribute.
2018-10-24 17:38:38 -04:00
bunnei
b723390ab1 Merge pull request #1571 from lioncash/debug-translate
graphic_breakpoints: Correct translation of strings in BreakpointModel's data() function
2018-10-24 17:37:18 -04:00
bunnei
ce2403d975 Merge pull request #1564 from lioncash/npad
npad: Remove unused controller variable from OnInit()
2018-10-24 17:36:55 -04:00
bunnei
d9590d7dfa Merge pull request #1568 from lioncash/dir
game_list: Use QFileInfo instead of common's file functions
2018-10-24 17:13:51 -04:00
bunnei
2694b43d3a Merge pull request #1567 from lioncash/translate
game_list: Make game list column headers translatable
2018-10-24 17:13:08 -04:00
bunnei
e6e17a3fc6 Merge pull request #1566 from lioncash/str
bootmanager: Use QStringLiteral instead of std::string to represent the window title.
2018-10-24 17:12:53 -04:00
bunnei
ddff188c65 Merge pull request #1563 from lioncash/frame
perf_stats: Remove unused variable within DoFrameLimiting()
2018-10-24 16:29:16 -04:00
bunnei
d14ba122e2 Merge pull request #1562 from lioncash/aoc
aoc_u: Make use of previously-unused CheckAOCTitleIDMatchesBase() function
2018-10-24 16:28:56 -04:00
bunnei
2eff8336f4 Merge pull request #1560 from lioncash/unused
maxwell_3d/decoders: Remove unused variables
2018-10-24 16:28:38 -04:00
bunnei
cdd499c261 Merge pull request #1561 from lioncash/fs
file_sys: Remove unused variables
2018-10-24 16:28:17 -04:00
bunnei
e65f5e4d66 Merge pull request #1559 from lioncash/log
logging/backend: Add missing services to the log filters
2018-10-24 16:28:01 -04:00
Lioncash
1fb4bebb63 kernel/errors: Remove now-unused, unnecessary, error codes
Now that we've gotten the innaccurate error codes out of the way, we can
finally toss away a bunch of these, trimming down the error codes to
ones that are actually used and knocking out two TODO comments.
2018-10-24 14:58:37 -04:00
Lioncash
239dfea34a kernel/shared_memory: Return ERR_INVALID_MEMORY_PERMISSIONS instead of ERR_INVALID_COMBINATION
This is more consistent with what the kernel does.
2018-10-24 14:54:36 -04:00
Lioncash
474bc29208 kernel/server_port: Simplify emptiness check within ShouldWait() 2018-10-24 14:24:36 -04:00
Lioncash
b703aba622 kernel/server_port: Change error case return value in Accept() to ERR_NOT_FOUND
This is what the kernel does in this instance.
2018-10-24 14:23:38 -04:00
Lioncash
afb7e5cc05 kernel/error: Remove leftover 3DS error codes
These are now entirely unused and can be removed.
2018-10-24 14:21:37 -04:00
Lioncash
fcf8f53a63 kernel/svc: Amend returned error code for invalid priorities in CreateThread
Like with the previous change, the kernel doesn't return NOT_AUTHORIZED
here. It returns INVALID_THREAD_PRIORITY.
2018-10-24 14:11:11 -04:00
Lioncash
77328b0f19 kernel/svc: Move and correct returned error code for invalid thread priorities in SetThreadPriority()
All priority checks are supposed to occur before checking the validity
of the thread handle, we're also not supposed to return
ERR_NOT_AUTHORIZED here.
2018-10-24 14:10:48 -04:00
Lioncash
c7c346a15d kernel/error: Add error code for invalid pointers
The kernel appears to return 0xE601 for this situation. Particularly in
svcWaitSynchronization, svcReplyAndReceive, and svcGetThreadContext
2018-10-24 13:41:32 -04:00
Lioncash
6df09f5b76 kernel/error: Add error code for closed sessions
The kernel appears to return 0xF601 for this case.
2018-10-24 13:38:39 -04:00
Lioncash
1edf8660bc game_list_worker: Use QString's formatting instead of fmt in FormatPatchNameVersions()
Using fmt here requires unnecessary string conversions back into
QString. Instead, we can just use QString's formatting and get the end
result of the formatting operation in the proper type.
2018-10-24 11:27:35 -04:00
Lioncash
4a31f99a02 profile_manager: Use std::optional instead of boost::optional
Now that we can actually use std::optional on macOS, we don't need to
continue using boost::optional here.
2018-10-24 11:06:52 -04:00
Lioncash
030847d5fa graphic_breakpoints: Correct translation of strings in BreakpointModel's data() function
tr() will not function properly on static/global data like this, as the
object is only ever constructed once, so the strings won't translate if
the language is changed without restarting the program, which is
undesirable. Instead we can just turn the map into a plain old function
that maps the values to their equivalent strings. This is also lessens
the memory allocated, since it's only allocating memory for the strings
themselves, and not an encompassing map as well.
2018-10-24 11:01:23 -04:00
Lioncash
bed2d6c425 yuzu/main: Notify user of loading errors with Amiibo data
We shouldn't silently continue if loading failed, since the general
assumption is that no messages showing up implicitly indicates success.
2018-10-24 10:39:31 -04:00
Mat M
77e705a8fa Merge pull request #1468 from DarkLordZach/profile-manager-ui
qt: Add UI to manage emulated user profiles
2018-10-24 10:10:29 -04:00
Zach Hilman
e7ac42677b configure_system: Clear current username before overwriting
Prevents bug where old username would remain if the new username was shorter in length.
2018-10-24 09:25:20 -04:00
Lioncash
a1c85b8c55 game_list: Use QFileInfo instead of common's file functions
We can just use the facilities that Qt provides instead of pulling in
stuff from common. While we're at it, we can also simplify the nearby
logging statement's argument by just calling .toStdString()
2018-10-24 08:40:22 -04:00
Lioncash
47f081d513 game_list: Make game list column headers translatable
These are user-facing strings, so they should be marked as translatable
2018-10-24 08:20:35 -04:00
Lioncash
2cbc284c2b bootmanager: Use QStringLiteral instead of std::string to represent the window title
This gets rid of an unnecessary type conversion. We can just use the
regular QStringLiteral to already format the string as the type
setWindowTitle accepts instead of converting from a std::string
instance.
2018-10-24 08:14:26 -04:00
Lioncash
6d27614994 time_stretch: Remove unused m_channel_count member variable
This is only stored to, but never read from.
2018-10-24 00:46:17 -04:00
Lioncash
93596d03ec npad: Remove unused controller variable from OnInit()
This also gets rid of variable shadowing related to the lambda parameter
a little bit below this code as well.
2018-10-24 00:38:03 -04:00
Lioncash
7c9f7aeacc perf_stats: Remove unused variable within DoFrameLimiting()
This hasn't been used since ba8ff096fd
2018-10-24 00:33:26 -04:00
Lioncash
6949f73149 yuzu/configuration/config: Use a std::unique_ptr for qt_config instead of a raw pointer
Same behavior, less code.
2018-10-24 00:24:02 -04:00
Lioncash
a3d1ede25f aoc_u: Make use of previously-unused CheckAOCTitleIDMatchesBase() function
We can just call the function instead of duplicating the code here. This
also prevents an unused function warning.

We also don't need to take the lambda capture by reference. It's just a
u64 value, so by value is fine here.
2018-10-24 00:13:08 -04:00
Lioncash
c7c594a6b8 vfs: Handle failure of file reading within VfsRawCopy()
Also gets rid of an unused variable.
2018-10-24 00:01:32 -04:00
Lioncash
c6529688fc key_manager: Remove unused variable in DeriveBase() 2018-10-24 00:00:12 -04:00
Lioncash
257b7bbfee decoders: Remove unused variable within SwizzledData() 2018-10-23 23:51:13 -04:00
Lioncash
a97cdb5eb4 maxwell_3d: Remove unused variable within ProcessQueryGet() 2018-10-23 23:50:16 -04:00
Lioncash
f80b80b922 logging/backend: Add missing services to the log filters
Just a few overlooked services.
2018-10-23 22:35:59 -04:00
Lioncash
6f5bede402 yuzu/configuration/config: Reorganize member variable and function layout
Makes the class layout consistent with the others.
2018-10-23 21:46:49 -04:00
bunnei
a94e5d9e68 Merge pull request #1551 from ogniK5377/improved-svcbreak
Added break types to svcBreak
2018-10-23 19:56:42 -04:00
Zach Hilman
bfad41b0c1 profile_manager: Create save data if it doesn't exist on use 2018-10-23 19:31:28 -04:00
Zach Hilman
45f2a2fe29 acc: Fix account UUID duplication error 2018-10-23 19:31:28 -04:00
Zach Hilman
e408bbceed configure_system: Clear selection after user delete 2018-10-23 19:31:28 -04:00
Zach Hilman
702622b8f1 profile_manager: Load user icons, names, and UUIDs from system save 2018-10-23 19:31:28 -04:00
Zach Hilman
19c5cf9c63 acc: Load user images from config dir 2018-10-23 19:31:28 -04:00
Zach Hilman
466960c8ab qt: Allow user to select emu user on open save data 2018-10-23 19:31:28 -04:00
Zach Hilman
b2a8209c5b qt: Add Profile Manager UI to system settings 2018-10-23 19:31:28 -04:00
Zach Hilman
d3fbf45705 am: Pass current user UUID to launch parameters 2018-10-23 19:31:28 -04:00
Zach Hilman
aeffd4b436 profile_manager: Load users from emulator settings 2018-10-23 19:31:28 -04:00
Zach Hilman
e7e3d5898e settings: Add users and current_user settings and remove username 2018-10-23 19:31:28 -04:00
David
50e4e81fd3 Added Amiibo support (#1390)
* Fixed conflict with nfp

* Few fixups for nfc

* Conflict 2

* Fixed AttachAvailabilityChangeEvent

* Conflict 3

* Fixed byte padding

* Refactored amiibo to not reside in "System"

* Removed remaining references of nfc from system

* used enum for Nfc GetStateOld

* Added missing newline

* Moved file operations to front end

* Conflict 4

* Amiibos now use structs and added mutexes

* Removed amiibo_path
2018-10-23 19:28:17 -04:00
bunnei
5edb2403c2 Merge pull request #1515 from DarkLordZach/dlc-lfs
patch_manager: Add support for LayeredFS on DLC RomFS
2018-10-23 19:26:57 -04:00
bunnei
fc9d8afead Merge pull request #1542 from lioncash/project
CMakeLists: Use PROJECT_SOURCE_DIR instead of CMAKE_SOURCE_DIR
2018-10-23 18:44:08 -04:00
bunnei
a5106fb9f5 Merge pull request #1553 from lioncash/mem
common: Remove memory_util.h/.cpp
2018-10-23 18:43:45 -04:00
bunnei
e61a62066a Merge pull request #1540 from lioncash/handle
kernel/process: Make the handle table per-process
2018-10-23 18:43:11 -04:00
FernandoS27
ed8ca608a0 Implement PointSize 2018-10-23 15:08:00 -04:00
FernandoS27
e0ea2f5f6e Fixed Layered Textures Loading and Cubemaps 2018-10-23 14:27:36 -04:00
Lioncash
289adf87ac CMakeLists: Remove EMU_ARCH_BITS definition
This was only ever used by the now-removed memory_util functions. Also,
given we don't plan to support 32-bit architectures, this is just a
leftover from citra at this point.
2018-10-23 12:24:43 -04:00
Lioncash
1291f3f820 common: Remove memory_util.cpp/.h
Everything from here is completely unused and also written with the
notion of supporting 32-bit architecture variants in mind. Given the
Switch itself is on a 64-bit architecture, we won't be supporting 32-bit
architectures. If we need specific allocation functions in the future,
it's likely more worthwhile to new functions for that purpose.
2018-10-23 12:21:34 -04:00
bunnei
e7e209d900 Merge pull request #1552 from FearlessTobi/port-4336
Port citra-emu/citra#4336: "Only redefine some 64-bit file operation for MSVC"
2018-10-23 10:23:41 -04:00
bunnei
5716496239 Merge pull request #1519 from ReinUsesLisp/vsetp
gl_shader_decompiler: Implement VSETP
2018-10-23 10:22:37 -04:00
bunnei
0f3d8c2574 Merge pull request #1539 from lioncash/dma
maxwell_dma: Silence compilation warnings
2018-10-23 10:22:12 -04:00
bunnei
75d807788c Merge pull request #1470 from FernandoS27/alpha_testing
Implemented Alpha Test using Shader Emulation
2018-10-23 10:21:30 -04:00
Weiyi Wang
d9ca6351dd cmake: mingw also needs _FILE_OFFSET_BITS=64 2018-10-23 15:11:25 +02:00
Weiyi Wang
2ff2732a78 only redefine 64 bit file operation for MSVC
MinGW provides POSIX functions
2018-10-23 15:11:18 +02:00
David Marcec
38cdb6744d Added assertion failed, reworked logging levels 2018-10-23 15:17:13 +11:00
ReinUsesLisp
7d6dca0d0a gl_shader_decompiler: Implement VSETP 2018-10-23 01:07:20 -03:00
ReinUsesLisp
5dfb43531c gl_shader_decompiler: Abstract VMAD into a video subset 2018-10-23 01:07:20 -03:00
David Marcec
8042731da9 Added break types to svcBreak
There seems to be more such as type 1, and 2. Unsure what these currently are but when a game hits them we can investigate and add the rest
2018-10-23 15:03:59 +11:00
bunnei
848a49112a Merge pull request #1512 from ReinUsesLisp/brk
gl_shader_decompiler: Implement PBK and BRK
2018-10-23 00:01:38 -04:00
bunnei
496d155d7b Merge pull request #1550 from FernandoS27/fmul32
Added Saturation to FMUL32I
2018-10-22 23:58:09 -04:00
bunnei
40c63073a9 Merge pull request #1543 from lioncash/target
CMakeLists: Use target_compile_definitions instead of add_definitions to define YUZU_ENABLE_COMPATIBILITY_REPORTING
2018-10-22 22:50:10 -04:00
bunnei
4cccfb4190 Merge pull request #1537 from lioncash/shader
gl_shader_decompiler: Minor changes
2018-10-22 22:49:49 -04:00
FernandoS27
259da93567 Added Saturation to FMUL32I 2018-10-22 20:22:15 -04:00
FernandoS27
8e1239fbc5 Assert that multiple render targets are not set while alpha testing 2018-10-22 15:35:45 -04:00
bunnei
ff6b2d4574 Merge pull request #1545 from DarkLordZach/psm
psm: Add psm service and stub commands 0 and 1
2018-10-22 15:27:05 -04:00
FernandoS27
59a004f915 Use standard UBO and fix/stylize the code 2018-10-22 15:07:33 -04:00
FernandoS27
17315cee16 Cache uniform locations and restructure the implementation 2018-10-22 15:07:32 -04:00
FernandoS27
bcb5b924fd Remove SyncAlphaTest and clang format 2018-10-22 15:07:31 -04:00
FernandoS27
7b39107e3a Added Alpha Func 2018-10-22 15:07:30 -04:00
FernandoS27
aa620c14af Implemented Alpha Testing 2018-10-22 15:07:30 -04:00
bunnei
65df593951 Merge pull request #1541 from lioncash/define
web_service/CMakeLists: Make the CPPHTTPLIB_OPENSSL_SUPPORT constrained to the web_service library only
2018-10-22 13:02:16 -04:00
bunnei
d9923b0dbc Merge pull request #1538 from lioncash/query
svc: Fix vma boundary check in svcQueryMemory
2018-10-22 12:55:36 -04:00
bunnei
1226a5706e Merge pull request #1547 from FernandoS27/fix-fset
Fixed FSETP and FSET
2018-10-22 12:53:47 -04:00
FernandoS27
5c5b4e8e7d Fixed FSETP and FSET 2018-10-22 11:31:17 -04:00
bunnei
fcad3a734d Merge pull request #1546 from lioncash/svc-again
service: Update service function tables
2018-10-22 11:06:55 -04:00
bunnei
38fa3aae73 Merge pull request #1548 from FernandoS27/fix-vao
Fixed VAOs Float types only returning GL_FLOAT
2018-10-22 10:58:47 -04:00
FernandoS27
e2416bbd1f Fixed VAOs Float types only returning GL_FLOAT in cases that they had to return GL_HALF_FLOAT 2018-10-22 09:27:00 -04:00
Zach Hilman
314a948373 psm: Stub GetChargerType
Used by LovePotion Lua Homebrew. Stubbed as connected to official Nintendo Switch dock.
2018-10-21 22:03:25 -04:00
bunnei
12fa570d49 Merge pull request #1544 from DarkLordZach/reinitialize-keys-tools
qt: Move Reinitialize Keys to Tools menu
2018-10-21 21:49:11 -04:00
Lioncash
ca5a93167e service: Add the basic skeleton for the NPNS services 2018-10-21 17:11:05 -04:00
Lioncash
981faea4d6 hid: Update service function table for hidbus
Updated based off information provided by Switchbrew.
2018-10-21 16:51:46 -04:00
Lioncash
5ea4cfd499 am: Add the basic skeleton for the tcap service
Added based off information provided by Switchbrew.
2018-10-21 16:50:17 -04:00
Lioncash
edb1c36a87 am: Update service function tables
Updated based off information from Switchbrew
2018-10-21 16:40:20 -04:00
Lioncash
ae7f55947e prepo: Update service function table.
Also introduces the new prepo:a2 service.

Updated based off information provided by Switchbrew.
2018-10-21 16:22:10 -04:00
Lioncash
a806c78a1a lbl: Update service function table names
Updated based off information provided by Switchbrew.
2018-10-21 16:15:32 -04:00
bunnei
f034121620 Merge pull request #1531 from ogniK5377/hid-fixes
Added auto controller switching to supported controllers and single joycon button rotation
2018-10-20 21:47:15 -04:00
Zach Hilman
a279d80a19 qt: Move Reinitialize Keys to Tools menu 2018-10-20 18:04:28 -04:00
Zach Hilman
10a2d20e26 psm: Stub GetBatteryChargePercentage
Used by LovePotion Lua Homebrew. Stubbed to return 100% charge.
2018-10-20 18:01:11 -04:00
Zach Hilman
3b8c0f8885 service: Add skeleton for psm service
Seems to be the power controller. Listed in switchbrew under the category PTM services.
2018-10-20 18:01:07 -04:00
Lioncash
c8beb665dc CMakeLists: Use PROJECT_SOURCE_DIR instead of CMAKE_SOURCE_DIR
This is more localized to what we want to enforce directory-wise with
the project. CMAKE_SOURCE_DIR indicates the root of the source tree, but
this would cause the wrong behavior if someone included yuzu as part of
a larger buildsystem (for whatever reason). Instead, we want to use the
directory where the "project(yuzu)" command was declared as the root
path reference.
2018-10-20 17:36:31 -04:00
Lioncash
98a27b1ec7 CMakeLists: Use target_compile_definitions instead of add_definitions to define YUZU_ENABLE_COMPATIBILITY_REPORTING
Keeps the definition constrained to the yuzu target and prevents
polluting anything else in the same directory (should that ever happen).
It also keeps it consistent with how the USE_DISCORD_PRESENCE definition
is introduced below it.
2018-10-20 17:28:29 -04:00
Lioncash
adb9eda105 web_service/CMakeLists: Make the CPPHTTPLIB_OPENSSL_SUPPORT constrained to the web_service library only
Given we link in httplib privately, we can also make the definition
enabling OpenSSL support private as well. Prevents leaking a definition
into other libraries that link with this one, like the core library.
2018-10-20 16:59:10 -04:00
Lioncash
90a981a03a kernel/process: Make the handle table per-process
In the kernel, there isn't a singular handle table that everything gets
tossed into or used, rather, each process gets its own handle table that
it uses. This currently isn't an issue for us, since we only execute one
process at the moment, but we may as well get this out of the way so
it's not a headache later on.
2018-10-20 16:38:32 -04:00
Lioncash
c1e5525fc6 engines/maxwell_*: Use nested namespace specifiers where applicable
These three source files are the only ones within the engines directory
that don't use nested namespaces. We may as well change these over to
keep things consistent.
2018-10-20 15:58:09 -04:00
Lioncash
d53c73adaa maxwell_dma: Make variables const where applicable within HandleCopy()
These are never modified, so we can make that assumption explicit.
2018-10-20 15:56:01 -04:00
Lioncash
dd1ee39426 maxwell_dma: Make FlushAndInvalidate's size parameter a u64
This prevents truncation warnings at the lambda's usage sites.
2018-10-20 15:54:45 -04:00
Lioncash
08e574eec4 maxwell_dma: Remove unused variables in HandleCopy()
These pointer variables are never used, so we can get rid of them.
2018-10-20 15:53:24 -04:00
Lioncash
896c0f61a0 svc: Fix vma boundary check in svcQueryMemory
This should be comparing against the queried process' vma_map, not the
current process'. The only reason this hasn't become an issue yet is we
currently only handle one process being active at any time.
2018-10-20 14:56:51 -04:00
Lioncash
8a86c8d48b gl_shader_decompiler: Allow std::move to function in SetPredicate
If the variable being moved is const, then std::move will always perform
a copy (since it can't actually move the data).
2018-10-20 14:25:15 -04:00
Lioncash
381baf783d gl_shader_decompiler: Get rid of variable shadowing warnings
A variable with the same name was previously declared in an outer scope.
2018-10-20 14:22:37 -04:00
Lioncash
61ef8af1e2 gl_shader_decompiler: Fix a few comment typos 2018-10-20 14:19:28 -04:00
bunnei
8dc7db7e33 Merge pull request #1535 from ReinUsesLisp/fixup-position
gl_shader_decompiler: Move position varying declaration back to gl_shader_gen
2018-10-20 12:35:10 -04:00
ReinUsesLisp
3ec795d95e gl_shader_decompiler: Move position varying declaration back to gl_shader_gen
The intention of declaring them in gl_shader_decompiler was to be able
to use blocks to implement geometry shaders. But that wasn't needed in
the end and it caused issues when both vertex stages were being used,
resulting in a redeclaration of "position".
2018-10-20 02:19:30 -03:00
David Marcec
a03600ba28 Added auto controller switching to supported controllers and single joycon button rotation
This is a subset of the better-hid-2 changes, this fixes input in various games which don't support dual joycons. This pr will search for the next best controller which is supported by the current game
2018-10-20 15:07:18 +11:00
bunnei
b1f8bff7db Merge pull request #1501 from ReinUsesLisp/half-float
gl_shader_decompiler: Implement H* instructions
2018-10-19 23:47:19 -04:00
bunnei
60317e6306 Merge pull request #1520 from lioncash/san
svc: Add missing sanitizing checks for MapSharedMemory/UnmapSharedMemory
2018-10-19 22:58:57 -04:00
bunnei
4849569565 Merge pull request #1517 from bunnei/dma
GPU/DMA: Flush the source region and invalidate the destination region when doing a DMA transfer.
2018-10-19 22:58:30 -04:00
bunnei
bf66930fb9 Merge pull request #1526 from lioncash/svc-id
service: Update function tables
2018-10-19 22:53:26 -04:00
bunnei
52b25e0fb9 Merge pull request #1530 from DarkLordZach/aoc-8
aoc_u: Stub GetAddOnContentListChangedEvent
2018-10-19 22:53:00 -04:00
bunnei
298ebf444f Merge pull request #1516 from lioncash/hid
hid: Minor cleanup-related changes
2018-10-19 22:52:31 -04:00
Zach Hilman
7e0d2fc994 aoc_u: Stub GetAddOnContentListChangedEvent
This event signals the game when new DLC is purchased from the eShop while the game is running. Since, for the forseeable future, yuzu will not have this ability, it seems safe to stub with a dummy event that will never fire. This is needed to boot Sonic Mania Plus (update v1.04).
2018-10-19 21:21:37 -04:00
Mat M
c91be25b93 Merge pull request #1529 from DarkLordZach/key-derivation-crash
crypto: Use compressed sizes in offset calculation for KIP decompression
2018-10-19 18:39:00 -04:00
Zach Hilman
0aef2b9c26 crypto: Use compressed sizes in offset calculation for KIP decompression
Fixes a fatal crash on start when deriving keys.
2018-10-19 18:37:58 -04:00
Zach Hilman
a25d79cfaa vfs: Remove InterpretAsDirectory and related functions
When writing VFS, it initally seemed useful to include a function to in-place convert container files into directories in one homogenous directory structure, but re-evaluating it now there have been plenty of chances to use it and there has always been a better way. Removing as it is unused and likely will not be used.
2018-10-19 14:02:07 -04:00
bunnei
12401a0d87 Merge pull request #1525 from ogniK5377/block-home
Home button blocking stub
2018-10-19 12:54:29 -04:00
David Marcec
7a7dad05c0 Stubbed home blocking
Needed by arms due to new hid rework
2018-10-20 00:01:10 +11:00
Lioncash
1833498617 es: Update service function tables
Updated based off information provided by Switchbrew.
2018-10-19 04:12:28 -04:00
Lioncash
a653be3510 audio: Update service function tables
Updated based off information provided by Switchbrew.
2018-10-19 04:09:12 -04:00
Lioncash
2b9fd23058 omm: Update service function tables
Updated based off information provided by Switchbrew.
2018-10-19 04:04:59 -04:00
Lioncash
4f52800822 nifm: Update service function tables
Updated based off information provided by switchbrew.
2018-10-19 04:00:41 -04:00
Lioncash
f6c5a48dd1 hid: Update service function tables
Updated based off information provided by Switchbrew.
2018-10-19 03:59:15 -04:00
Lioncash
d0cda7fe40 nim: Add the basic skeleton of the nim:eca service
Added based off information provided by Switchbrew
2018-10-19 03:46:18 -04:00
Lioncash
d16bafc99d ns: Update service function table
Updated based off information provided by Switchbrew.
2018-10-19 03:41:38 -04:00
Lioncash
a056b284cf set_cal: Update service function table
Updated based on information from Switchbrew.
2018-10-19 03:26:56 -04:00
bunnei
7e665c2721 GPU: Improved implementation of maxwell DMA (Subv). 2018-10-18 22:41:53 -04:00
bunnei
bcde71d4d9 decoders: Introduce functions for un/swizzling subrects. 2018-10-18 22:41:43 -04:00
bunnei
a5d853a9f8 GPU: Invalidate destination address of kepler_memory writes. 2018-10-18 22:41:13 -04:00
bunnei
6b333d862b fermi_2d: Add support for more accurate surface copies. 2018-10-18 22:41:12 -04:00
bunnei
fdd82b754a Merge pull request #1523 from lioncash/lock
svc: Add missing error checks in svcArbitrateLock/svcArbitrateUnlock
2018-10-18 21:50:45 -04:00
bunnei
7f152f2273 Merge pull request #1511 from lioncash/content
content_archive: Minor reorganization changes
2018-10-18 21:48:09 -04:00
bunnei
e5d428cf1e Merge pull request #1521 from ogniK5377/imp-mmu
Used better names for mm:u and fixed a bad stub
2018-10-18 21:46:59 -04:00
bunnei
0291a86f60 Merge pull request #1522 from lioncash/core
core: Remove unnecessary assert in ArmInterface()
2018-10-18 21:46:19 -04:00
Lioncash
4b5ae8dbaa svc: Check for word alignment of addresses within svcArbitrateLock/svcArbitrateUnlock
The kernel itself checks whether or not the provided addresses are word
aligned before continuing, so we should be doing the same.
2018-10-18 13:01:29 -04:00
Lioncash
541e9624eb common: Add function for checking word alignment to alignment.h
This will be used in a following change to svcArbitrateLock() and
svcArbitrateUnlock()
2018-10-18 12:58:27 -04:00
Lioncash
d27f4a4928 common: Move Is4KBAligned() to alignment.h
Aligning on 4KB pages isn't a Switch-specific thing, so this can be
moved to common so it can be used with other things as well.
2018-10-18 12:57:02 -04:00
Lioncash
f109615be0 core: Remove unnecessary assert in ArmInterface()
CpuCore already does this sort of checking, so we can just call that
instead of duplicating the assertions.
2018-10-18 12:07:25 -04:00
bunnei
d4ff4152ad Merge pull request #1510 from lioncash/xci
XCI: Add function for checking the existence of the program NCA
2018-10-18 11:51:47 -04:00
bunnei
6acd8d166a Merge pull request #1505 from FernandoS27/tex-3d
Implemented 3D Textures
2018-10-18 11:50:42 -04:00
David Marcec
98c7a6d622 Used better names for mm:u and fixed bad stub
InitializeWithId needs to return an id which is a u32 which should be a non zero value
2018-10-19 01:09:34 +11:00
Lioncash
33830aa65a svc: Add missing sanitizing checks for MapSharedMemory/UnmapSharedMemory
Now that the changes clarifying the address spaces has been merged, we
can wrap the checks that the kernel performs when mapping shared memory
(and other forms of memory) into its own helper function and then use
those within MapSharedMemory and UnmapSharedMemory to complete the
sanitizing checks that are supposed to be done.
2018-10-18 02:01:21 -04:00
Lioncash
452aa30cb7 hid/controller: Remove unused header inclusions
swap.h only needs to be present in the header for the type aliases and
definitions, it's not actually needed in the cpp files though. input.h
is just unused entirely in xpad.h
2018-10-17 20:52:45 -04:00
Lioncash
7f52dc1790 hid/controller/npad: Remove unused dump_idx member variable
Given it's unused, we may as well toss it.
2018-10-17 20:52:45 -04:00
Lioncash
7eb2328d8e hid/controller/npad: Remove unnecessary semicolon from the closing brace of LedPattern's constructor 2018-10-17 20:52:45 -04:00
Lioncash
929ed59f1f hid/controller/npad: Remove #pragma once from the cpp file
This is only useful in headers.
2018-10-17 20:52:45 -04:00
Lioncash
aeda743446 hid/controller/npad: Move npad_id_list into the cpp file
This is just a lookup table, and since it's private, there's nothing
really stateful about it, so we can just move it into the cpp file.
2018-10-17 20:52:45 -04:00
Lioncash
aeca224890 hid/controller/npad: Remove unnecessary const from void return type
This literally does nothing.
2018-10-17 20:52:45 -04:00
Lioncash
46202e984e hid/controller: Default the destructors of all controller types in the cpp file
These classes are non-trivial and are definitely going to be changed in
the future, so we default these to prevent issues with forward
declarations, and to keep the compiler from inlining tear-down code.
2018-10-17 20:52:43 -04:00
Lioncash
119b47f366 controller_base: Default the base class constructor and destructor in the cpp file
The destructor doesn't need to be a pure-virtual function.
2018-10-17 20:51:54 -04:00
ReinUsesLisp
41fb25349a gl_shader_decompiler: Implement PBK and BRK 2018-10-17 21:30:45 -03:00
bunnei
7dee60d7d2 Merge pull request #1444 from ogniK5377/better-hid
"Better Hid" Rework Part 1
2018-10-17 20:25:17 -04:00
bunnei
77e2d68df7 Merge pull request #1489 from FernandoS27/fix-tlds
shader_decompiler: Fix TLDS
2018-10-17 18:58:38 -04:00
FernandoS27
caaa9914fd Clang format and other fixes 2018-10-17 18:52:11 -04:00
FernandoS27
cb9fdc7a26 Implement Reinterpret Surface, to accurately blit 3D textures 2018-10-17 18:52:10 -04:00
FernandoS27
dbc34db6ce Implement GetInRange in the Rasterizer Cache 2018-10-17 18:52:10 -04:00
FernandoS27
fd9e2d0073 Implement 3D Textures 2018-10-17 18:52:08 -04:00
bunnei
f912a82a8e Merge pull request #1497 from bunnei/flush-framebuffers
Implement flushing in the rasterizer cache
2018-10-17 18:40:34 -04:00
bunnei
6e8752881c Merge pull request #1498 from lioncash/aslr
svc: Clarify enum values for AddressSpaceBaseAddr and AddressSpaceSize in svcGetInfo()
2018-10-17 18:31:51 -04:00
bunnei
86dcf2942b Merge pull request #1496 from FernandoS27/tex-array
Implement Arrays on Tex Instruction
2018-10-17 18:30:44 -04:00
Zach Hilman
9d0fb0f815 qt: Add support for dumping a DLC Data RomFS 2018-10-17 18:27:29 -04:00
bunnei
afe22d8405 Merge pull request #1509 from DarkLordZach/device-save-data
savedata_factory: Add DeviceSaveData and fix TemporaryStorage
2018-10-17 18:22:05 -04:00
bunnei
648b55c6b9 gl_rasterizer_cache: Remove unnecessary block_depth=1 on Flush. 2018-10-17 18:20:15 -04:00
bunnei
2a035a1f6f gl_rasterizer_cache: Remove unnecessary temporary buffer with unswizzle. 2018-10-17 18:19:35 -04:00
Zach Hilman
59044862a9 registered_cache: Deduplicate results of ListEntry and ListEntryFilter
Prevents a Entry from appearing in the list twice if the user has it installed in two places (e.g. User NAND and SDMC)
2018-10-17 14:04:18 -04:00
David Marcec
8144fa42bd Using dual joycons as the default controller
Reason for the change is to allow both docked and undocked mode to work
2018-10-18 00:11:47 +11:00
Zach Hilman
780c21ab2d fsp_srv: Apply patches to Data storage in OpenDataStorageByDataId 2018-10-17 09:04:20 -04:00
Zach Hilman
d8273c3857 patch_manager: Add support for using LayeredFS with Data 2018-10-17 09:03:56 -04:00
bunnei
43b9494a0f gl_rasterizer_cache: Use AccurateCopySurface for use_accurate_gpu_emulation. 2018-10-16 17:20:49 -04:00
bunnei
ee7c2dbf5a config: Rename use_accurate_framebuffers -> use_accurate_gpu_emulation.
- This will be used as a catch-all for slow-but-accurate GPU emulation paths.
2018-10-16 17:02:29 -04:00
bunnei
91602de7f2 rasterizer_cache: Refactor to support in-order flushing. 2018-10-16 16:51:53 -04:00
Lioncash
871350ae35 content_archive: Simpify assignment of bktr_base_romfs in the constructor
std::move doesn't actually dereference the data, so it doesn't matter
whether or not the type is null.
2018-10-16 13:22:31 -04:00
Lioncash
441b5b97bd content_archive: Make IsValidNCA() an internally linked function
This is only ever used within the cpp file, so it can just be an
internal function.
2018-10-16 13:22:31 -04:00
Lioncash
53e77ffbfe content_archive: Simplify rights ID check
This is the same as using std::any_of with an inverted predicate.
2018-10-16 13:22:31 -04:00
Lioncash
d6604fa765 content_archive: Split loading into separate functions
The constructor alone is pretty large, the reading code should be split
into its consistuent parts to make it easier to understand it without
having to build a mental model of a 300+ line function.
2018-10-16 13:22:28 -04:00
Lioncash
4783ad54de content_archive: Pass and take NCASectionHeader instance by reference
Each header is 512 bytes in size, which is kind of an excessive amount
to copy all the time when it's possible to avoid doing so.
2018-10-16 12:08:17 -04:00
Lioncash
73e1e929a2 XCI: Add function for checking the existence of the program NCA
The only reason the getter existed was to check whether or not the
program NCA was null. Instead, we can just provide a function to query
for the existence of it, instead of exposing it entirely.
2018-10-16 11:36:58 -04:00
bunnei
0e59291310 gl_rasterizer_cache: Refactor to only call GetRegionEnd on surface creation. 2018-10-16 11:31:02 -04:00
bunnei
949d7832fa gl_rasterizer_cache: Only flush when use_accurate_framebuffers is enabled. 2018-10-16 11:31:02 -04:00
bunnei
5f79ba04bd gl_rasterizer_cache: Separate guest and host surface size managment. 2018-10-16 11:31:01 -04:00
bunnei
58be4dff79 gl_rasterizer_cache: Rename GetGLBytesPerPixel to GetBytesPerPixel.
- This does not really have anything to do with OpenGL.
2018-10-16 11:31:01 -04:00
bunnei
cf7b46c101 gl_rasterizer_cache: Remove unused FlushSurface method. 2018-10-16 11:31:01 -04:00
bunnei
3afdfd7bfa gl_rasterizer: Implement flushing. 2018-10-16 11:31:01 -04:00
bunnei
b4e29ccb81 gl_rasterizer_cache: Remove usage of Memory::Read/Write functions.
- These cannot be used within the cache, as they change cache state.
2018-10-16 11:31:00 -04:00
bunnei
4e9683e9d5 gl_rasterizer_cache: Clamp cached surface size to mapped GPU region size. 2018-10-16 11:31:00 -04:00
bunnei
37575eae65 memory_manager: Add a method for querying the end of a mapped GPU region. 2018-10-16 11:31:00 -04:00
bunnei
0be7e82289 rasterizer_cache: Reintroduce method for flushing. 2018-10-16 11:31:00 -04:00
bunnei
9b929e934b gl_rasterizer_cache: Reintroduce code for handling swizzle and flush to guest RAM. 2018-10-16 11:30:59 -04:00
bunnei
78f2a6a9e1 Merge pull request #1443 from DarkLordZach/lower-loader-logs-1
content_archive/patch_manager: Lower log levels to eliminate some unnecessary logs
2018-10-16 11:26:54 -04:00
David
92d8ad3770 Implement VI ConvertScalingMode (#1475)
* Implement VI ConvertScalingMode

* Fixed push enum

* Scale mode now uses Nintendo scale mode as an enum as well
2018-10-16 11:25:42 -04:00
bunnei
88b8383da2 Merge pull request #1502 from lioncash/unique
core: Convert shared_ptr instances into unique_ptr instances where applicable for System and Cpu
2018-10-16 11:21:42 -04:00
bunnei
59c1ca8b0c Merge pull request #1508 from lioncash/unique-reg
file_sys/registered_cache: Use unique_ptr and regular pointers instead of shared_ptrs where applicable
2018-10-16 11:21:13 -04:00
bunnei
d6e390bc5c Merge pull request #1507 from FearlessTobi/port-4327
Port citra-emu/citra#4327: "travis: Ignore binary files when checking for trailing whitespace"
2018-10-16 10:42:10 -04:00
Zach Hilman
9d4e6176eb savedata_factory: Add TemporaryStorage SaveDataSpaceId
Required for TemporaryStorage saves (in addition to SaveDataType)
2018-10-16 10:20:04 -04:00
Zach Hilman
74890cf2da savedata_factory: Add support for DeviceSaveData
Uses the same path as SaveData except with UID 0. Adds a warning if UID is not 0.
2018-10-16 10:19:21 -04:00
Lioncash
39ae73b356 file_sys/registered_cache: Use unique_ptr and regular pointers instead of shared_ptrs where applicable
The data retrieved in these cases are ultimately chiefly owned by either
the RegisteredCache instance itself, or the filesystem factories. Both
these should live throughout the use of their contained data. If they
don't, it should be considered an interface/design issue, and using
shared_ptr instances here would mask that, as the data would always be
prolonged after the main owner's lifetime ended.

This makes the lifetime of the data explicit and makes it harder to
accidentally create cyclic references. It also makes the interface
slightly more flexible than the previous API, as a shared_ptr can be
created from a unique_ptr, but not the other way around, so this allows
for that use-case if it ever becomes necessary in some form.
2018-10-16 09:38:52 -04:00
Cameron Cawley
41674d20ac travis: Ignore binary files when checking for trailing whitespace 2018-10-16 14:48:37 +02:00
bunnei
548958bcaf Merge pull request #1473 from lioncash/cmake
web_service: Make linkage of web_service-related externals and the library private
2018-10-15 21:33:32 -04:00
bunnei
870c18b078 Merge pull request #1487 from lioncash/maybe-unused
yuzu/main: Apply the [[maybe_unused]] attribute to the parameter of SetDiscordEnabled
2018-10-15 21:33:14 -04:00
bunnei
89fe950d3c Merge pull request #1504 from lioncash/constant
file_sys/control_metadata: Get rid of magic constants
2018-10-15 21:32:13 -04:00
Lioncash
76fc8b59b2 file_sys/control_metadata: Get rid of magic constants
These are just the size of the data being passed in, so we can specify
that via the size() member function.
2018-10-15 20:11:44 -04:00
bunnei
9b21fbd1eb Merge pull request #1494 from DarkLordZach/aoc-signature-fixes
aoc: Fix various bugs in current AOC implementation
2018-10-15 18:34:02 -04:00
bunnei
50e6205c21 Merge pull request #1499 from lioncash/nro
nro/nso: Minor error handling changes
2018-10-15 17:48:36 -04:00
bunnei
7665411317 Merge pull request #1500 from DarkLordZach/key-derivation-6.0.0
crypto: Various crypto fixes for quickstart guide
2018-10-15 17:48:13 -04:00
Lioncash
bed872ed38 nso: Return an optional address from LoadModule
If a malformed NSO is attempted to be loaded, we shouldn't continue
onwards. We should be reporting an error and bailing out.
2018-10-15 17:02:11 -04:00
bunnei
123df8f7d7 Merge pull request #1503 from ReinUsesLisp/misc-vc
video_core: Minor style changes
2018-10-15 16:38:20 -04:00
ReinUsesLisp
936c36a514 shader_bytecode: Add Control Code enum 0xf
Control Code 0xf means to unconditionally execute the instruction. This
value is passed to most BRA, EXIT and SYNC instructions (among others)
but this may not always be the case.
2018-10-15 15:36:47 -03:00
ReinUsesLisp
b461342a84 gl_shader_decompiler: Fixup style inconsistencies 2018-10-15 15:35:26 -03:00
ReinUsesLisp
27916764b1 gl_rasterizer: Silence implicit cast warning in glBindBufferRange 2018-10-15 15:26:50 -03:00
Lioncash
5484742fda core_cpu: Make Cpu scheduler instances unique_ptrs instead of shared_ptrs 2018-10-15 14:15:56 -04:00
Lioncash
59f872a8e0 core: Make the live Cpu instances unique_ptrs instead of shared_ptrs
There's no need for shared ownership here, as the only owning class
instance of those Cpu instances is the System class itself. We can also
make the thread_to_cpu map use regular pointers instead of shared_ptrs,
given that the Cpu instances will always outlive the cases where they're
used with that map.
2018-10-15 14:15:56 -04:00
Lioncash
aeadbfa790 core: Make the exclusive monitor a unique_ptr instead of a shared_ptr
Like the barrier, this is owned entirely by the System and will always
outlive the encompassing state, so shared ownership semantics aren't
necessary here.
2018-10-15 14:15:50 -04:00
Lioncash
c34efbbd60 core: Make CPUBarrier a unique_ptr instead of a shared_ptr
This will always outlive the Cpu instances, since it's destroyed after
we destroy the Cpu instances on shutdown, so there's no need for shared
ownership semantics here.
2018-10-15 09:11:47 -04:00
ReinUsesLisp
6312eec5ef gl_shader_decompiler: Implement HSET2_R 2018-10-15 02:55:51 -03:00
ReinUsesLisp
4fc8ad67bf gl_shader_decompiler: Implement HSETP2_R 2018-10-15 02:55:51 -03:00
ReinUsesLisp
3d65aa4caf gl_shader_decompiler: Implement HFMA2 instructions 2018-10-15 02:55:51 -03:00
ReinUsesLisp
d93cdc2750 gl_shader_decompiler: Implement HADD2_IMM and HMUL2_IMM 2018-10-15 02:07:16 -03:00
ReinUsesLisp
d46e2a6e7a gl_shader_decompiler: Implement non-immediate HADD2 and HMUL2 instructions 2018-10-15 02:04:31 -03:00
ReinUsesLisp
08d751d882 gl_shader_decompiler: Setup base for half float unpacking and setting 2018-10-15 01:58:30 -03:00
Zach Hilman
720d36ca71 crypto: Various crypto fixes for quickstart guide 2018-10-14 21:57:52 -04:00
Lioncash
bb9cf8a127 nso: Make LoadModule take a VfsFile by const reference 2018-10-14 20:38:19 -04:00
Lioncash
0732786ddc nro: Make LoadNro take a VfsFile by const reference
This function doesn't need to care about ownership semantics, so we can
just pass it a reference to the file itself, rather than a
std::shared_ptr alias.
2018-10-14 20:24:18 -04:00
Lioncash
90f8474fc1 svc: Clarify enum values for AddressSpaceBaseAddr and AddressSpaceSize in svcGetInfo()
So, one thing that's puzzled me is why the kernel seemed to *not* use
the direct code address ranges in some cases for some service functions.
For example, in svcMapMemory, the full address space width is compared
against for validity, but for svcMapSharedMemory, it compares against
0xFFE00000, 0xFF8000000, and 0x7FF8000000 as upper bounds, and uses
either 0x200000 or 0x8000000 as the lower-bounds as the beginning of the
compared range. Coincidentally, these exact same values are also used in
svcGetInfo, and also when initializing the user address space, so this
is actually retrieving the ASLR extents, not the extents of the address
space in general.
2018-10-14 20:11:16 -04:00
Zach Hilman
5737441374 aoc: Read DLC base title ID from RegisteredCache
Falls back to title ID + 0x1000, which is what HOS does.
2018-10-14 18:58:14 -04:00
bunnei
b3cca34f50 Merge pull request #1486 from lioncash/file
key_manager/partition_data_manager: Minor changes
2018-10-14 14:46:47 -04:00
bunnei
3203193a67 Merge pull request #1490 from lioncash/boot
yuzu/main: Simplify OnMenuLoadFile()
2018-10-14 14:44:49 -04:00
bunnei
14286f70f0 Merge pull request #1488 from Hexagon12/astc-types
video_core: Added ASTC 5x4; 8x5 types
2018-10-14 14:44:24 -04:00
bunnei
0d2ba0a320 Merge pull request #1491 from lioncash/reference
filesystem: Make CreateFactories() and InstallInterface() take a VfsFilesystem by reference
2018-10-14 14:42:57 -04:00
bunnei
b82bbfba77 Merge pull request #1480 from FernandoS27/neue-swizzle
Introduce 3D Swizzle seamlessly
2018-10-14 14:42:38 -04:00
bunnei
2f8ca32020 Merge pull request #1492 from lioncash/proc
svc: Implement svcGetProcessInfo
2018-10-14 14:37:58 -04:00
bunnei
b183ce4365 Merge pull request #1495 from ogniK5377/break-stop
Stop all threads on svcBreak
2018-10-14 14:31:35 -04:00
FernandoS27
1d6559fbd3 Implement Arrays on Tex Instruction 2018-10-14 13:31:02 -04:00
David Marcec
92fae7e1ab Stop all threads on svcBreak
This should help diagnose crashes easier and prevent many users thinking that a game is still running when in fact it's just an audio thread still running(this is typically not killed when svcBreak is hit since the game expects us to do this)
2018-10-14 18:14:51 +11:00
Zach Hilman
7e2096db8a aoc: Return size in ListAddOnContent 2018-10-13 22:52:54 -04:00
FernandoS27
d880b77698 Fix TLDS 2018-10-13 22:14:25 -04:00
FernandoS27
331ce2942c Shorten the implementation of 3D swizzle to only 3 functions 2018-10-13 20:58:00 -04:00
Lioncash
1c7a7ed79b svc: Implement svcGetProcessInfo
A fairly basic service function, which only appears to currently support
retrieving the process state. This also alters the ProcessStatus enum to
contain all of the values that a kernel process seems to be able of
reporting with regards to state.
2018-10-13 17:00:43 -04:00
FernandoS27
1ff20d8538 Fix a Crash on Zelda BotW and Splatoon 2, and simplified LoadGLBuffer 2018-10-13 16:11:11 -04:00
FernandoS27
e0ca938b22 Propagate depth and depth_block on modules using decoders 2018-10-13 15:25:18 -04:00
FernandoS27
d4ae43f9c1 Remove old Swizzle algorithms and use 3d Swizzle 2018-10-13 15:25:17 -04:00
FernandoS27
4d959c6bdc Implement Precise 3D Swizzle 2018-10-13 15:25:16 -04:00
FernandoS27
736db284d2 Implement Fast 3D Swizzle 2018-10-13 15:25:15 -04:00
Lioncash
0149162dba filesystem: Make CreateFactories() and InstallInterface() take a VfsFilesystem instance by reference
Neither of these functions alter the ownership of the provided pointer,
so we can simply make the parameters a reference rather than a direct
shared pointer alias. This way we also disallow passing incorrect memory values like
nullptr.
2018-10-13 11:36:35 -04:00
Lioncash
a4c57436fc yuzu/main: Simplify OnMenuLoadFile()
We can utilize QStringList's join() function to perform all of the
appending in a single function call.

While we're at it, make the extension list a single translatable string
and add a disambiguation comment to explain to translators what %1
actually is.
2018-10-13 10:35:18 -04:00
Lioncash
53a0221484 yuzu/main: Apply the [[maybe_unused]] attribute to the parameter of SetDiscordEnabled()
Depending on whether or not USE_DISCORD_PRESENCE is defined, the "state"
parameter can be used or unused. If USE_DISCORD_PRESENCE is not defined,
the parameter will be considered unused, which can lead to compiler
warnings. So, we can explicitly mark it with [[maybe_unused]] to inform
the compiler that this is intentional.
2018-10-13 10:10:29 -04:00
Hexagon12
cbf723896f Added ASTC 5x4; 8x5 2018-10-13 17:10:26 +03:00
Lioncash
6467b01de2 partition_data_manager: Reserve and insert data within output vector in DecryptPackage2()
We can just reserve the memory then perform successive insertions
instead of needing to use memcpy. This also avoids the need to zero out
the output vector's memory before performing the insertions.

We can also std::move the output std::vector into the destination so
that we don't need to make a completely new copy of the vector, getting
rid of an unnecessary allocation.

Additionally, we can use iterators to determine the beginning and end
ranges of the std::vector instances that comprise the output vector, as
the end of one range just becomes the beginning for the next successive
range, and since std::vector's iterator constructor copies data within
the range [begin, end), this is more straightforward and gets rid of the
need to have an offset variable that keeps getting incremented to
determine where to do the next std::memcpy.
2018-10-13 09:50:08 -04:00
Lioncash
781fd7983c partition_data_manager: Remove unused std::map instance within DecryptPackage2()
Aside from emplacing elements into the map, the map itself is never
actually queried for contained data.
2018-10-13 09:27:12 -04:00
Lioncash
e0c76226ad partition_data_manager: Take package2_keys by const reference
These are only ever read from, so we don't need to make a copy of all
the keys here.
2018-10-13 09:24:41 -04:00
Lioncash
3d9df49619 partition_data_manager: Move IV data to where it's needed in DecryptPackage2()
Given it's only used in one spot and has a fairly generic name, we can
just specify it directly in the function call. This also the benefit of
automatically moving it.
2018-10-13 09:20:21 -04:00
Lioncash
bc2196bb09 partition_data_manager: Remove commented out code
Commented out code shouldn't be left in without a reason indicating why
in a comment.
2018-10-13 09:17:02 -04:00
Lioncash
6da2ed4232 key_manager/partition_data_manager: Silence truncation compiler warnings 2018-10-13 09:13:19 -04:00
Lioncash
f56a8da46a partition_data_manager: Dehardcode array bounds
Instead, we can make it part of the type and make named variables for
them, so they only require one definition (and if they ever change for
whatever reason, they only need to be changed in one spot).
2018-10-13 08:52:37 -04:00
Lioncash
d257a3b56c partition_data_manager: Take VirtualFile by const reference in constructor
Given the VirtualFile instance isn't stored into the class as a data
member, or written to, this can just be turned into a const reference,
as the constructor doesn't need to make a copy of it.
2018-10-13 08:39:05 -04:00
Lioncash
e96d69c328 partition_data_manager: Amend constructor initializer list order
Orders the members in the exact order they would be initialized. This
also prevents compiler warnings about this sort of thing.
2018-10-13 08:36:26 -04:00
Lioncash
aaca7543f0 partition_data_manager: Remove unused includes
Gets unused includes out of the headers and moves them into the cpp file
if they're used there instead.
2018-10-13 08:33:49 -04:00
Lioncash
06898263f6 key_manager: Use std::vector's insert() instead of std::copy with a back_inserter
If the data is unconditionally being appended to the back of a
std::vector, we can just directly insert it there without the need to
insert all of the elements one-by-one with a std::back_inserter.
2018-10-13 08:29:35 -04:00
Lioncash
e70c08b543 key_manager: Brace long conditional body
If a conditional (or it's body) travels more than one line, it should be
braced.
2018-10-13 08:24:21 -04:00
Lioncash
ef5639bfbb key_manager: Don't assume file seeks and reads will always succeed
Given the filesystem should always be assumed to be volatile, we should
check and bail out if a seek operation isn't successful. This'll prevent
potentially writing/returning garbage data from the function in rare
cases.

This also allows removing a check to see if an offset is within the
bounds of a file before perfoming a seek operation. If a seek is
attempted beyond the end of a file, it will fail, so this essentially
combines two checks into one in one place.
2018-10-13 08:24:18 -04:00
Lioncash
82ea1cf35a key_manager: Remove unnecessary seek in DeriveSDSeed()
Given the file is opened a few lines above and no operations are done,
other than check if the file is in a valid state, the read/write pointer
will always be at the beginning of the file.
2018-10-13 08:08:44 -04:00
Zach Hilman
f61379f8d2 patch_manager: Move non-Program RomFS patch log to Debug
Normal Program-type patches will still be logged to aid in debugging, but for others (mainly Control), it was moved to Debug.
2018-10-12 23:27:19 -04:00
Zach Hilman
90c07e0d33 content_archive: Move get key log to Trace level
Avoids printing live keys in the general log.
2018-10-12 23:25:59 -04:00
bunnei
1584fb6b38 Merge pull request #1409 from DarkLordZach/key-derivation
crypto: Add support for full key derivation
2018-10-12 22:55:49 -04:00
bunnei
c2aa4293ec Merge pull request #1483 from lioncash/codeset
kernel/process: Make CodeSet a regular non-inherited object
2018-10-12 22:52:12 -04:00
bunnei
38b027aa81 Merge pull request #1484 from FernandoS27/calculate-size
Implemented helper function to correctly calculate a texture's size
2018-10-12 21:12:53 -04:00
bunnei
ffcda6c08e Merge pull request #1481 from lioncash/typo
svc: Fix typos in sanitizing checks for MapMemory/UnmapMemory
2018-10-12 20:46:55 -04:00
FernandoS27
97b6405a17 Implemented helper function to correctly calculate a texture's size 2018-10-12 14:21:53 -04:00
bunnei
2946d4bdbe Merge pull request #1467 from ogniK5377/svcbreak-type-fix
Fixed incorrect types for svcBreak
2018-10-12 12:08:08 -04:00
Lioncash
1abed2f4c4 kernel/process: Make CodeSet a regular non-inherited object
These only exist to ferry data into a Process instance and end up going
out of scope quite early. Because of this, we can just make it a plain
struct for holding things and just std::move it into the relevant
function. There's no need to make this inherit from the kernel's Object
type.
2018-10-12 12:07:32 -04:00
bunnei
0f7ab3e21a Merge pull request #1478 from ogniK5377/remap-invalidhandle-remap
Passing an invalid nmap handle to Remap should throw an error
2018-10-12 12:07:14 -04:00
bunnei
f9d03b1d41 Merge pull request #1482 from lioncash/init
thread: Remove unnecessary memset from ResetThreadContext()
2018-10-12 12:06:51 -04:00
bunnei
dc328440c8 Merge pull request #1479 from ogniK5377/nmap-revamped
Added error codes for nvmap
2018-10-12 12:06:22 -04:00
Lioncash
b492d43e63 thread: Remove unnecessary memset from ResetThreadContext()
Regular value initialization is adequate here for zeroing out data. It
also has the benefit of not invoking undefined behavior if a non-trivial
type is ever added to the struct for whatever reason.
2018-10-12 10:57:31 -04:00
David Marcec
4d2de6564f Returned an error before processing other remaps 2018-10-12 17:10:41 +11:00
David Marcec
c55b5de0fb Made the minimum alignment more clear 2018-10-12 17:06:46 +11:00
Lioncash
4ccf30dfaa svc: Fix typos in sanitizing checks for MapMemory/UnmapMemory 2018-10-12 01:48:26 -04:00
David Marcec
98b760c645 Wip 2018-10-12 16:28:00 +11:00
bunnei
9bf409f275 Merge pull request #1474 from ogniK5377/hwopus-decodeinterleavedwithperformance
HwOpus, Implemented DecodeInterleavedWithPerformance
2018-10-11 16:52:13 -04:00
bunnei
3fd26b7147 Merge pull request #1472 from lioncash/san
svc: Add missing address range sanitizing checks to MapMemory/UnmapMemory
2018-10-11 16:51:41 -04:00
bunnei
bc293e1751 Merge pull request #1476 from bunnei/fix-unmap-flush
nvhost_as_gpu: Flush/invalidate CPU VAddr on UnmapBuffer.
2018-10-11 16:51:28 -04:00
bunnei
83ac3e6395 Merge pull request #1477 from ReinUsesLisp/vmad
gl_shader_decompiler: Implement VMAD
2018-10-11 16:51:09 -04:00
David Marcec
85b0d9a7be Dynamically decide handheld variant based on supported npad id priority
Kirby input still doesn't work, should fix a lot of other games
2018-10-12 02:56:49 +11:00
David Marcec
c7763603ef Added error codes for nvmap 2018-10-11 23:06:34 +11:00
David Marcec
5dd538cace Passing an invalid nmap handle to Remap should throw an error
Added error for invalid nmap handles
2018-10-11 20:32:21 +11:00
ReinUsesLisp
17290a4416 gl_shader_decompiler: Implement VMAD 2018-10-11 04:15:10 -03:00
bunnei
bf795edac4 nvhost_as_gpu: Flush CPU VAddr on UnmapBuffer. 2018-10-11 00:19:36 -04:00
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
Lioncash
72e9cb523e svc: Add missing address range sanitizing checks to MapMemory/UnmapMemory
This adds the missing address range checking that the service functions
do before attempting to map or unmap memory. Given that both service
functions perform the same set of checks in the same order, we can wrap
these into a function and just call it from both functions, which
deduplicates a little bit of code.
2018-10-10 20:30:49 -04:00
David Marcec
9e924f2ef2 Added BeginPermitVibrationSession and EndPermitVibrationSession
Used by Mario Party
2018-10-11 00:58:47 +11:00
David Marcec
3d75c9cd7a Added GetLedPattern and HandheldVariant
HandheldVariant is for specific games which expect handheld controllers to be at position 8(kirby), however this doesn't fix all games as some games require handhelds to be at position 0(snipperclips)
2018-10-10 21:38:43 +11:00
David Marcec
46cdeb4549 Kirby expects handheld controllers to be at position 8 2018-10-10 14:21:56 +11:00
David Marcec
f43815af5d Added the ability to "disconnect" individual npads
Fixes arms
2018-10-10 13:15:39 +11:00
David Marcec
b79c294c02 Removed unneeded forward declarations 2018-10-10 13:15:37 +11:00
David Marcec
5857aea94e Addressed changes for better hid 2018-10-10 13:15:37 +11:00
David Marcec
56f35ab262 "Better Hid" rework part 1 2018-10-10 13:15:35 +11:00
David Marcec
2db37ddea9 Changed all casts in svc_wrap.h to be static_cast instead 2018-10-10 12:49:08 +11:00
David Marcec
09b6dda8f0 Use a better name than "dont_kill_application"
signal_debugger seems like a more fitting name
2018-10-10 12:27:44 +11:00
David Marcec
a4412c8e22 Fixed incorrect types for svcBreak
svcBreak reason should be a u32, not a u64.
2018-10-10 12:23:50 +11:00
Zach Hilman
3ec054643e partition_data_manager: Rename system files for hekate
x
2018-10-07 13:16:23 -04:00
Zach Hilman
8f958b89e7 qt: Add rederive keyset menu option 2018-10-07 13:16:04 -04:00
Zach Hilman
3edafc6802 qt: Add key derivation progress bar on initial setup 2018-10-07 13:15:11 -04:00
Zach Hilman
29dc6f4519 crypto: Add PartitionDataManager
Keeps track of system files for key derivation
2018-10-07 13:15:11 -04:00
Zach Hilman
4aad010f7a key_manager: Add support for loading keys from partition data 2018-10-07 13:15:11 -04:00
Zach Hilman
d041d6231c key_manager: Add ETicket key derivation
Derives titlekeys
2018-10-07 13:15:11 -04:00
Zach Hilman
a57aac5772 key_manager: Add base key derivation
Derives master keys, game encryption keys, and package1/2 keys
2018-10-07 13:15:11 -04:00
Zach Hilman
d7398283e3 key_manager: Add BIS key getter 2018-10-07 13:15:11 -04:00
Zach Hilman
d6a0d5d432 key_manager: Add support for more keys
TSEC, SBK, BIS, and other Sources for proper derivation
2018-10-07 13:15:11 -04:00
Zach Hilman
c79d2ca6cf key_manager: Add keyblob support 2018-10-07 13:15:11 -04:00
Zach Hilman
e4602748d6 key_manager: Add support for crypto revisions past 04 2018-10-07 13:15:11 -04:00
Zach Hilman
9e34303fb9 key_manager: Add support for comments in keyfiles 2018-10-07 13:15:11 -04:00
Zach Hilman
1fa6ee4723 vfs: Move forward declarations to separate file 2018-10-07 13:15:11 -04:00
Zach Hilman
ce05df0a6d key_manager: Add support for console-specific keyfile 2018-10-07 13:15:11 -04:00
Zach Hilman
721632fe66 key_manager: Rename KEK to Kek 2018-10-07 13:15:11 -04:00
Zach Hilman
89ad82ce5c externals/mbedtls: Enable CMAC module
Required for keyblob verification
2018-10-07 13:15:11 -04:00
Carl Kenner
f5f6292810 logging: Add DebuggerBackend for logging to Visual Studio 2018-10-07 13:24:04 +10:30
264 changed files with 12240 additions and 4615 deletions

View File

@@ -1,6 +1,6 @@
#!/bin/bash -ex
if grep -nr '\s$' src *.yml *.txt *.md Doxyfile .gitignore .gitmodules .travis* dist/*.desktop \
if grep -nrI '\s$' src *.yml *.txt *.md Doxyfile .gitignore .gitmodules .travis* dist/*.desktop \
dist/*.svg dist/*.xml; then
echo Trailing whitespace found, aborting
exit 1

View File

@@ -23,21 +23,21 @@ 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)
if(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/hooks/pre-commit)
message(STATUS "Copying pre-commit hook")
file(COPY hooks/pre-commit
DESTINATION ${CMAKE_SOURCE_DIR}/.git/hooks)
DESTINATION ${PROJECT_SOURCE_DIR}/.git/hooks)
endif()
# Sanity check : Check that all submodules are present
# =======================================================================
function(check_submodules_present)
file(READ "${CMAKE_SOURCE_DIR}/.gitmodules" gitmodules)
file(READ "${PROJECT_SOURCE_DIR}/.gitmodules" gitmodules)
string(REGEX MATCHALL "path *= *[^ \t\r\n]*" gitmodules ${gitmodules})
foreach(module ${gitmodules})
string(REGEX REPLACE "path *= *" "" module ${module})
if (NOT EXISTS "${CMAKE_SOURCE_DIR}/${module}/.git")
if (NOT EXISTS "${PROJECT_SOURCE_DIR}/${module}/.git")
message(FATAL_ERROR "Git submodule ${module} not found. "
"Please run: git submodule update --init --recursive")
endif()
@@ -45,17 +45,17 @@ function(check_submodules_present)
endfunction()
check_submodules_present()
configure_file(${CMAKE_SOURCE_DIR}/dist/compatibility_list/compatibility_list.qrc
${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
configure_file(${PROJECT_SOURCE_DIR}/dist/compatibility_list/compatibility_list.qrc
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
COPYONLY)
if (ENABLE_COMPATIBILITY_LIST_DOWNLOAD AND NOT EXISTS ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
if (ENABLE_COMPATIBILITY_LIST_DOWNLOAD AND NOT EXISTS ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
message(STATUS "Downloading compatibility list for yuzu...")
file(DOWNLOAD
https://api.yuzu-emu.org/gamedb/
"${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json" SHOW_PROGRESS)
"${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json" SHOW_PROGRESS)
endif()
if (NOT EXISTS ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
file(WRITE ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json "")
if (NOT EXISTS ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
file(WRITE ${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.json "")
endif()
# Detect current compilation architecture and create standard definitions
@@ -170,7 +170,7 @@ endif()
# On modern Unixes, this is typically already the case. The lone exception is
# glibc, which may default to 32 bits. glibc allows this to be configured
# by setting _FILE_OFFSET_BITS.
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
if(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR MINGW)
add_definitions(-D_FILE_OFFSET_BITS=64)
endif()
@@ -178,10 +178,6 @@ endif()
set_property(DIRECTORY APPEND PROPERTY
COMPILE_DEFINITIONS $<$<CONFIG:Debug>:_DEBUG> $<$<NOT:$<CONFIG:Debug>>:NDEBUG>)
math(EXPR EMU_ARCH_BITS ${CMAKE_SIZEOF_VOID_P}*8)
add_definitions(-DEMU_ARCH_BITS=${EMU_ARCH_BITS})
# System imported libraries
# ======================
@@ -189,13 +185,13 @@ find_package(Boost 1.63.0 QUIET)
if (NOT Boost_FOUND)
message(STATUS "Boost 1.63.0 or newer not found, falling back to externals")
set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/externals/boost")
set(BOOST_ROOT "${PROJECT_SOURCE_DIR}/externals/boost")
set(Boost_NO_SYSTEM_PATHS OFF)
find_package(Boost QUIET REQUIRED)
endif()
# Output binaries to bin/
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
# Prefer the -pthread flag on Linux.
set(THREADS_PREFER_PTHREAD_FLAG ON)
@@ -264,7 +260,7 @@ if (YUZU_USE_BUNDLED_UNICORN)
endif()
set(UNICORN_FOUND YES)
set(UNICORN_PREFIX ${CMAKE_SOURCE_DIR}/externals/unicorn)
set(UNICORN_PREFIX ${PROJECT_SOURCE_DIR}/externals/unicorn)
set(LIBUNICORN_LIBRARY "${UNICORN_PREFIX}/${UNICORN_LIB_NAME}" CACHE PATH "Path to Unicorn library" FORCE)
set(LIBUNICORN_INCLUDE_DIR "${UNICORN_PREFIX}/include" CACHE PATH "Path to Unicorn headers" FORCE)
set(UNICORN_DLL_DIR "${UNICORN_PREFIX}/" CACHE PATH "Path to unicorn dynamic library" FORCE)
@@ -356,12 +352,12 @@ set(CLANG_FORMAT_POSTFIX "-6.0")
find_program(CLANG_FORMAT
NAMES clang-format${CLANG_FORMAT_POSTFIX}
clang-format
PATHS ${CMAKE_BINARY_DIR}/externals)
PATHS ${PROJECT_BINARY_DIR}/externals)
# if find_program doesn't find it, try to download from externals
if (NOT CLANG_FORMAT)
if (WIN32)
message(STATUS "Clang format not found! Downloading...")
set(CLANG_FORMAT "${CMAKE_BINARY_DIR}/externals/clang-format${CLANG_FORMAT_POSTFIX}.exe")
set(CLANG_FORMAT "${PROJECT_BINARY_DIR}/externals/clang-format${CLANG_FORMAT_POSTFIX}.exe")
file(DOWNLOAD
https://github.com/yuzu-emu/ext-windows-bin/raw/master/clang-format${CLANG_FORMAT_POSTFIX}.exe
"${CLANG_FORMAT}" SHOW_PROGRESS
@@ -377,7 +373,7 @@ if (NOT CLANG_FORMAT)
endif()
if (CLANG_FORMAT)
set(SRCS ${CMAKE_SOURCE_DIR}/src)
set(SRCS ${PROJECT_SOURCE_DIR}/src)
set(CCOMMENT "Running clang format against all the .h and .cpp files in src/")
if (WIN32)
add_custom_target(clang-format
@@ -450,10 +446,10 @@ endif()
# http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
# http://standards.freedesktop.org/shared-mime-info-spec/shared-mime-info-spec-latest.html
if(ENABLE_QT AND UNIX AND NOT APPLE)
install(FILES "${CMAKE_SOURCE_DIR}/dist/yuzu.desktop"
install(FILES "${PROJECT_SOURCE_DIR}/dist/yuzu.desktop"
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/applications")
install(FILES "${CMAKE_SOURCE_DIR}/dist/yuzu.svg"
install(FILES "${PROJECT_SOURCE_DIR}/dist/yuzu.svg"
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/scalable/apps")
install(FILES "${CMAKE_SOURCE_DIR}/dist/yuzu.xml"
install(FILES "${PROJECT_SOURCE_DIR}/dist/yuzu.xml"
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/mime/packages")
endif()

0
git
View File

View File

@@ -121,7 +121,8 @@ CubebSink::CubebSink(std::string target_device_name) {
const auto collection_end{collection.device + collection.count};
const auto device{
std::find_if(collection.device, collection_end, [&](const cubeb_device_info& info) {
return target_device_name == info.friendly_name;
return info.friendly_name != nullptr &&
target_device_name == info.friendly_name;
})};
if (device != collection_end) {
output_device = device->devid;

View File

@@ -11,7 +11,6 @@
#include "audio_core/stream.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/microprofile.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
#include "core/settings.h"
@@ -104,10 +103,7 @@ void Stream::PlayNextBuffer() {
CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {});
}
MICROPROFILE_DEFINE(AudioOutput, "Audio", "ReleaseActiveBuffer", MP_RGB(100, 100, 255));
void Stream::ReleaseActiveBuffer() {
MICROPROFILE_SCOPE(AudioOutput);
ASSERT(active_buffer);
released_buffers.push(std::move(active_buffer));
release_callback();

View File

@@ -10,8 +10,7 @@
namespace AudioCore {
TimeStretcher::TimeStretcher(u32 sample_rate, u32 channel_count)
: m_sample_rate(sample_rate), m_channel_count(channel_count) {
TimeStretcher::TimeStretcher(u32 sample_rate, u32 channel_count) : m_sample_rate{sample_rate} {
m_sound_touch.setChannels(channel_count);
m_sound_touch.setSampleRate(sample_rate);
m_sound_touch.setPitch(1.0);
@@ -33,10 +32,10 @@ std::size_t TimeStretcher::Process(const s16* in, std::size_t num_in, s16* out,
// We were given actual_samples number of samples, and num_samples were requested from us.
double current_ratio = static_cast<double>(num_in) / static_cast<double>(num_out);
const double max_latency = 1.0; // seconds
const double max_latency = 0.25; // seconds
const double max_backlog = m_sample_rate * max_latency;
const double backlog_fullness = m_sound_touch.numSamples() / max_backlog;
if (backlog_fullness > 5.0) {
if (backlog_fullness > 4.0) {
// Too many samples in backlog: Don't push anymore on
num_in = 0;
}
@@ -50,7 +49,7 @@ std::size_t TimeStretcher::Process(const s16* in, std::size_t num_in, s16* out,
// This low-pass filter smoothes out variance in the calculated stretch ratio.
// The time-scale determines how responsive this filter is.
constexpr double lpf_time_scale = 2.0; // seconds
constexpr double lpf_time_scale = 0.712; // seconds
const double lpf_gain = 1.0 - std::exp(-time_delta / lpf_time_scale);
m_stretch_ratio += lpf_gain * (current_ratio - m_stretch_ratio);

View File

@@ -27,7 +27,6 @@ public:
private:
u32 m_sample_rate;
u32 m_channel_count;
soundtouch::SoundTouch m_sound_touch;
double m_stretch_ratio = 1.0;
};

View File

@@ -64,8 +64,6 @@ add_library(common STATIC
logging/text_formatter.cpp
logging/text_formatter.h
math_util.h
memory_util.cpp
memory_util.h
microprofile.cpp
microprofile.h
microprofileui.h

View File

@@ -19,4 +19,16 @@ constexpr T AlignDown(T value, std::size_t size) {
return static_cast<T>(value - value % size);
}
template <typename T>
constexpr bool Is4KBAligned(T value) {
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
return (value & 0xFFF) == 0;
}
template <typename T>
constexpr bool IsWordAligned(T value) {
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
return (value & 0b11) == 0;
}
} // namespace Common

View File

@@ -15,21 +15,24 @@
#ifdef _WIN32
#include <windows.h>
// windows.h needs to be included before other windows headers
#include <commdlg.h> // for GetSaveFileName
#include <direct.h> // getcwd
#include <direct.h> // getcwd
#include <io.h>
#include <shellapi.h>
#include <shlobj.h> // for SHGetFolderPath
#include <tchar.h>
#include "common/string_util.h"
// 64 bit offsets for windows
#ifdef _MSC_VER
// 64 bit offsets for MSVC
#define fseeko _fseeki64
#define ftello _ftelli64
#define atoll _atoi64
#define fileno _fileno
#endif
// 64 bit offsets for MSVC and MinGW. MinGW also needs this for using _wstat64
#define stat _stat64
#define fstat _fstat64
#define fileno _fileno
#else
#ifdef __APPLE__
#include <sys/param.h>

View File

@@ -12,7 +12,8 @@
#include <thread>
#include <vector>
#ifdef _WIN32
#include <share.h> // For _SH_DENYWR
#include <share.h> // For _SH_DENYWR
#include <windows.h> // For OutputDebugStringA
#else
#define _SH_DENYWR 0
#endif
@@ -139,12 +140,18 @@ void FileBackend::Write(const Entry& entry) {
if (!file.IsOpen() || bytes_written > MAX_BYTES_WRITTEN) {
return;
}
bytes_written += file.WriteString(FormatLogMessage(entry) + '\n');
bytes_written += file.WriteString(FormatLogMessage(entry).append(1, '\n'));
if (entry.log_level >= Level::Error) {
file.Flush();
}
}
void DebuggerBackend::Write(const Entry& entry) {
#ifdef _WIN32
::OutputDebugStringA(FormatLogMessage(entry).append(1, '\n').c_str());
#endif
}
/// Macro listing all log classes. Code should define CLS and SUB as desired before invoking this.
#define ALL_LOG_CLASSES() \
CLS(Log) \
@@ -196,6 +203,7 @@ void FileBackend::Write(const Entry& entry) {
SUB(Service, NFP) \
SUB(Service, NIFM) \
SUB(Service, NIM) \
SUB(Service, NPNS) \
SUB(Service, NS) \
SUB(Service, NVDRV) \
SUB(Service, PCIE) \
@@ -204,10 +212,12 @@ void FileBackend::Write(const Entry& entry) {
SUB(Service, PM) \
SUB(Service, PREPO) \
SUB(Service, PSC) \
SUB(Service, PSM) \
SUB(Service, SET) \
SUB(Service, SM) \
SUB(Service, SPL) \
SUB(Service, SSL) \
SUB(Service, TCAP) \
SUB(Service, Time) \
SUB(Service, USB) \
SUB(Service, VI) \

View File

@@ -103,6 +103,20 @@ private:
std::size_t bytes_written;
};
/**
* Backend that writes to Visual Studio's output window
*/
class DebuggerBackend : public Backend {
public:
static const char* Name() {
return "debugger";
}
const char* GetName() const override {
return Name();
}
void Write(const Entry& entry) override;
};
void AddBackend(std::unique_ptr<Backend> backend);
void RemoveBackend(std::string_view backend_name);

View File

@@ -83,6 +83,7 @@ enum class Class : ClassType {
Service_NFP, ///< The NFP service
Service_NIFM, ///< The NIFM (Network interface) service
Service_NIM, ///< The NIM service
Service_NPNS, ///< The NPNS service
Service_NS, ///< The NS services
Service_NVDRV, ///< The NVDRV (Nvidia driver) service
Service_PCIE, ///< The PCIe service
@@ -91,10 +92,12 @@ enum class Class : ClassType {
Service_PM, ///< The PM service
Service_PREPO, ///< The PREPO (Play report) service
Service_PSC, ///< The PSC service
Service_PSM, ///< The PSM service
Service_SET, ///< The SET (Settings) service
Service_SM, ///< The SM (Service manager) service
Service_SPL, ///< The SPL service
Service_SSL, ///< The SSL service
Service_TCAP, ///< The TCAP service.
Service_Time, ///< The time service
Service_USB, ///< The USB (Universal Serial Bus) service
Service_VI, ///< The VI (Video interface) service

View File

@@ -1,177 +0,0 @@
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/logging/log.h"
#include "common/memory_util.h"
#ifdef _WIN32
#include <windows.h>
// Windows.h needs to be included before psapi.h
#include <psapi.h>
#include "common/common_funcs.h"
#include "common/string_util.h"
#else
#include <cstdlib>
#include <sys/mman.h>
#endif
#if !defined(_WIN32) && defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT)
#include <unistd.h>
#define PAGE_MASK (getpagesize() - 1)
#define round_page(x) ((((unsigned long)(x)) + PAGE_MASK) & ~(PAGE_MASK))
#endif
// This is purposely not a full wrapper for virtualalloc/mmap, but it
// provides exactly the primitive operations that Dolphin needs.
void* AllocateExecutableMemory(std::size_t size, bool low) {
#if defined(_WIN32)
void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
#else
static char* map_hint = nullptr;
#if defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT)
// This OS has no flag to enforce allocation below the 4 GB boundary,
// but if we hint that we want a low address it is very likely we will
// get one.
// An older version of this code used MAP_FIXED, but that has the side
// effect of discarding already mapped pages that happen to be in the
// requested virtual memory range (such as the emulated RAM, sometimes).
if (low && (!map_hint))
map_hint = (char*)round_page(512 * 1024 * 1024); /* 0.5 GB rounded up to the next page */
#endif
void* ptr = mmap(map_hint, size, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_ANON | MAP_PRIVATE
#if defined(ARCHITECTURE_x86_64) && defined(MAP_32BIT)
| (low ? MAP_32BIT : 0)
#endif
,
-1, 0);
#endif /* defined(_WIN32) */
#ifdef _WIN32
if (ptr == nullptr) {
#else
if (ptr == MAP_FAILED) {
ptr = nullptr;
#endif
LOG_ERROR(Common_Memory, "Failed to allocate executable memory");
}
#if !defined(_WIN32) && defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT)
else {
if (low) {
map_hint += size;
map_hint = (char*)round_page(map_hint); /* round up to the next page */
}
}
#endif
#if EMU_ARCH_BITS == 64
if ((u64)ptr >= 0x80000000 && low == true)
LOG_ERROR(Common_Memory, "Executable memory ended up above 2GB!");
#endif
return ptr;
}
void* AllocateMemoryPages(std::size_t size) {
#ifdef _WIN32
void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE);
#else
void* ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
if (ptr == MAP_FAILED)
ptr = nullptr;
#endif
if (ptr == nullptr)
LOG_ERROR(Common_Memory, "Failed to allocate raw memory");
return ptr;
}
void* AllocateAlignedMemory(std::size_t size, std::size_t alignment) {
#ifdef _WIN32
void* ptr = _aligned_malloc(size, alignment);
#else
void* ptr = nullptr;
#ifdef ANDROID
ptr = memalign(alignment, size);
#else
if (posix_memalign(&ptr, alignment, size) != 0)
LOG_ERROR(Common_Memory, "Failed to allocate aligned memory");
#endif
#endif
if (ptr == nullptr)
LOG_ERROR(Common_Memory, "Failed to allocate aligned memory");
return ptr;
}
void FreeMemoryPages(void* ptr, std::size_t size) {
if (ptr) {
#ifdef _WIN32
if (!VirtualFree(ptr, 0, MEM_RELEASE))
LOG_ERROR(Common_Memory, "FreeMemoryPages failed!\n{}", GetLastErrorMsg());
#else
munmap(ptr, size);
#endif
}
}
void FreeAlignedMemory(void* ptr) {
if (ptr) {
#ifdef _WIN32
_aligned_free(ptr);
#else
free(ptr);
#endif
}
}
void WriteProtectMemory(void* ptr, std::size_t size, bool allowExecute) {
#ifdef _WIN32
DWORD oldValue;
if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READ : PAGE_READONLY, &oldValue))
LOG_ERROR(Common_Memory, "WriteProtectMemory failed!\n{}", GetLastErrorMsg());
#else
mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_EXEC) : PROT_READ);
#endif
}
void UnWriteProtectMemory(void* ptr, std::size_t size, bool allowExecute) {
#ifdef _WIN32
DWORD oldValue;
if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE,
&oldValue))
LOG_ERROR(Common_Memory, "UnWriteProtectMemory failed!\n{}", GetLastErrorMsg());
#else
mprotect(ptr, size,
allowExecute ? (PROT_READ | PROT_WRITE | PROT_EXEC) : PROT_WRITE | PROT_READ);
#endif
}
std::string MemUsage() {
#ifdef _WIN32
#pragma comment(lib, "psapi")
DWORD processID = GetCurrentProcessId();
HANDLE hProcess;
PROCESS_MEMORY_COUNTERS pmc;
std::string Ret;
// Print information about the memory usage of the process.
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID);
if (nullptr == hProcess)
return "MemUsage Error";
if (GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc)))
Ret = fmt::format("{} K", Common::ThousandSeparate(pmc.WorkingSetSize / 1024, 7));
CloseHandle(hProcess);
return Ret;
#else
return "";
#endif
}

View File

@@ -1,21 +0,0 @@
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <cstddef>
#include <string>
void* AllocateExecutableMemory(std::size_t size, bool low = true);
void* AllocateMemoryPages(std::size_t size);
void FreeMemoryPages(void* ptr, std::size_t size);
void* AllocateAlignedMemory(std::size_t size, std::size_t alignment);
void FreeAlignedMemory(void* ptr);
void WriteProtectMemory(void* ptr, std::size_t size, bool executable = false);
void UnWriteProtectMemory(void* ptr, std::size_t size, bool allowExecute = false);
std::string MemUsage();
inline int GetPageSize() {
return 4096;
}

View File

@@ -153,6 +153,7 @@ struct VisitorInterface : NonCopyable {
/// Completion method, called once all fields have been visited
virtual void Complete() = 0;
virtual bool SubmitTestcase() = 0;
};
/**
@@ -178,6 +179,9 @@ struct NullVisitor : public VisitorInterface {
void Visit(const Field<std::chrono::microseconds>& /*field*/) override {}
void Complete() override {}
bool SubmitTestcase() override {
return false;
}
};
/// Appends build-specific information to the given FieldCollection,

View File

@@ -5,6 +5,7 @@
#pragma once
#include <string>
#include "common/common_types.h"
namespace Common {
struct WebResult {

View File

@@ -18,6 +18,8 @@ add_library(core STATIC
crypto/encryption_layer.h
crypto/key_manager.cpp
crypto/key_manager.h
crypto/partition_data_manager.cpp
crypto/partition_data_manager.h
crypto/ctr_encryption_layer.cpp
crypto/ctr_encryption_layer.h
crypto/xts_encryption_layer.cpp
@@ -70,6 +72,7 @@ add_library(core STATIC
file_sys/vfs_real.cpp
file_sys/vfs_real.h
file_sys/vfs_static.h
file_sys/vfs_types.h
file_sys/vfs_vector.cpp
file_sys/vfs_vector.h
file_sys/xts_archive.cpp
@@ -153,6 +156,8 @@ add_library(core STATIC
hle/service/am/omm.h
hle/service/am/spsm.cpp
hle/service/am/spsm.h
hle/service/am/tcap.cpp
hle/service/am/tcap.h
hle/service/aoc/aoc_u.cpp
hle/service/aoc/aoc_u.h
hle/service/apm/apm.cpp
@@ -233,6 +238,24 @@ add_library(core STATIC
hle/service/hid/irs.h
hle/service/hid/xcd.cpp
hle/service/hid/xcd.h
hle/service/hid/controllers/controller_base.cpp
hle/service/hid/controllers/controller_base.h
hle/service/hid/controllers/debug_pad.cpp
hle/service/hid/controllers/debug_pad.h
hle/service/hid/controllers/gesture.cpp
hle/service/hid/controllers/gesture.h
hle/service/hid/controllers/keyboard.cpp
hle/service/hid/controllers/keyboard.h
hle/service/hid/controllers/mouse.cpp
hle/service/hid/controllers/mouse.h
hle/service/hid/controllers/npad.cpp
hle/service/hid/controllers/npad.h
hle/service/hid/controllers/stubbed.cpp
hle/service/hid/controllers/stubbed.h
hle/service/hid/controllers/touchscreen.cpp
hle/service/hid/controllers/touchscreen.h
hle/service/hid/controllers/xpad.cpp
hle/service/hid/controllers/xpad.h
hle/service/lbl/lbl.cpp
hle/service/lbl/lbl.h
hle/service/ldn/ldn.cpp
@@ -259,6 +282,8 @@ add_library(core STATIC
hle/service/nifm/nifm.h
hle/service/nim/nim.cpp
hle/service/nim/nim.h
hle/service/npns/npns.cpp
hle/service/npns/npns.h
hle/service/ns/ns.cpp
hle/service/ns/ns.h
hle/service/ns/pl_u.cpp
@@ -306,6 +331,8 @@ add_library(core STATIC
hle/service/prepo/prepo.h
hle/service/psc/psc.cpp
hle/service/psc/psc.h
hle/service/ptm/psm.cpp
hle/service/ptm/psm.h
hle/service/service.cpp
hle/service/service.h
hle/service/set/set.cpp
@@ -397,8 +424,8 @@ 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)
add_definitions(-DENABLE_WEB_SERVICE)
target_link_libraries(core PUBLIC json-headers web_service)
target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE)
target_link_libraries(core PRIVATE web_service)
endif()
if (ARCHITECTURE_x86_64)

View File

@@ -144,7 +144,7 @@ std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const {
// Multi-process state
config.processor_id = core_index;
config.global_monitor = &exclusive_monitor->monitor;
config.global_monitor = &exclusive_monitor.monitor;
// System registers
config.tpidrro_el0 = &cb->tpidrro_el0;
@@ -171,10 +171,9 @@ void ARM_Dynarmic::Step() {
cb->InterpreterFallback(jit->GetPC(), 1);
}
ARM_Dynarmic::ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
std::size_t core_index)
ARM_Dynarmic::ARM_Dynarmic(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)} {
exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {
ThreadContext ctx{};
inner_unicorn.SaveContext(ctx);
PageTableChanged();

View File

@@ -23,7 +23,7 @@ class DynarmicExclusiveMonitor;
class ARM_Dynarmic final : public ARM_Interface {
public:
ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, std::size_t core_index);
ARM_Dynarmic(ExclusiveMonitor& exclusive_monitor, std::size_t core_index);
~ARM_Dynarmic();
void MapBackingMemory(VAddr address, std::size_t size, u8* memory,
@@ -62,7 +62,7 @@ private:
ARM_Unicorn inner_unicorn;
std::size_t core_index;
std::shared_ptr<DynarmicExclusiveMonitor> exclusive_monitor;
DynarmicExclusiveMonitor& exclusive_monitor;
Memory::PageTable* current_page_table = nullptr;
};

View File

@@ -71,9 +71,9 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
}
/// Runs a CPU core while the system is powered on
void RunCpuCore(std::shared_ptr<Cpu> cpu_state) {
void RunCpuCore(Cpu& cpu_state) {
while (Core::System::GetInstance().IsPoweredOn()) {
cpu_state->RunLoop(true);
cpu_state.RunLoop(true);
}
}
} // Anonymous namespace
@@ -95,7 +95,7 @@ struct System::Impl {
status = ResultStatus::Success;
// Update thread_to_cpu in case Core 0 is run from a different host thread
thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0];
thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0].get();
if (GDBStub::IsServerEnabled()) {
GDBStub::HandlePacket();
@@ -139,16 +139,16 @@ struct System::Impl {
auto main_process = Kernel::Process::Create(kernel, "main");
kernel.MakeCurrentProcess(main_process.get());
cpu_barrier = std::make_shared<CpuBarrier>();
cpu_barrier = std::make_unique<CpuBarrier>();
cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size());
for (std::size_t index = 0; index < cpu_cores.size(); ++index) {
cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index);
cpu_cores[index] = std::make_unique<Cpu>(*cpu_exclusive_monitor, *cpu_barrier, index);
}
telemetry_session = std::make_unique<Core::TelemetrySession>();
service_manager = std::make_shared<Service::SM::ServiceManager>();
Service::Init(service_manager, virtual_filesystem);
Service::Init(service_manager, *virtual_filesystem);
GDBStub::Init();
renderer = VideoCore::CreateRenderer(emu_window);
@@ -160,12 +160,12 @@ struct System::Impl {
// Create threads for CPU cores 1-3, and build thread_to_cpu map
// CPU core 0 is run on the main thread
thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0];
thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0].get();
if (Settings::values.use_multi_core) {
for (std::size_t index = 0; index < cpu_core_threads.size(); ++index) {
cpu_core_threads[index] =
std::make_unique<std::thread>(RunCpuCore, cpu_cores[index + 1]);
thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1];
std::make_unique<std::thread>(RunCpuCore, std::ref(*cpu_cores[index + 1]));
thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1].get();
}
}
@@ -185,7 +185,7 @@ struct System::Impl {
LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
return ResultStatus::ErrorGetLoader;
}
std::pair<boost::optional<u32>, Loader::ResultStatus> system_mode =
std::pair<std::optional<u32>, Loader::ResultStatus> system_mode =
app_loader->LoadKernelSystemMode();
if (system_mode.second != Loader::ResultStatus::Success) {
@@ -245,6 +245,7 @@ struct System::Impl {
for (auto& cpu_core : cpu_cores) {
cpu_core.reset();
}
cpu_exclusive_monitor.reset();
cpu_barrier.reset();
// Shutdown kernel and core timing
@@ -282,9 +283,9 @@ struct System::Impl {
std::unique_ptr<VideoCore::RendererBase> renderer;
std::unique_ptr<Tegra::GPU> gpu_core;
std::shared_ptr<Tegra::DebugContext> debug_context;
std::shared_ptr<ExclusiveMonitor> cpu_exclusive_monitor;
std::shared_ptr<CpuBarrier> cpu_barrier;
std::array<std::shared_ptr<Cpu>, NUM_CPU_CORES> cpu_cores;
std::unique_ptr<ExclusiveMonitor> cpu_exclusive_monitor;
std::unique_ptr<CpuBarrier> cpu_barrier;
std::array<std::unique_ptr<Cpu>, NUM_CPU_CORES> cpu_cores;
std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads;
std::size_t active_core{}; ///< Active core, only used in single thread mode
@@ -298,7 +299,7 @@ struct System::Impl {
std::string status_details = "";
/// Map of guest threads to CPU cores
std::map<std::thread::id, std::shared_ptr<Cpu>> thread_to_cpu;
std::map<std::thread::id, Cpu*> thread_to_cpu;
Core::PerfStats perf_stats;
Core::FrameLimiter frame_limiter;
@@ -311,6 +312,10 @@ Cpu& System::CurrentCpuCore() {
return impl->CurrentCpuCore();
}
const Cpu& System::CurrentCpuCore() const {
return impl->CurrentCpuCore();
}
System::ResultStatus System::RunLoop(bool tight_loop) {
return impl->RunLoop(tight_loop);
}
@@ -341,7 +346,11 @@ PerfStatsResults System::GetAndResetPerfStats() {
return impl->GetAndResetPerfStats();
}
Core::TelemetrySession& System::TelemetrySession() const {
TelemetrySession& System::TelemetrySession() {
return *impl->telemetry_session;
}
const TelemetrySession& System::TelemetrySession() const {
return *impl->telemetry_session;
}
@@ -349,17 +358,28 @@ ARM_Interface& System::CurrentArmInterface() {
return CurrentCpuCore().ArmInterface();
}
std::size_t System::CurrentCoreIndex() {
const ARM_Interface& System::CurrentArmInterface() const {
return CurrentCpuCore().ArmInterface();
}
std::size_t System::CurrentCoreIndex() const {
return CurrentCpuCore().CoreIndex();
}
Kernel::Scheduler& System::CurrentScheduler() {
return *CurrentCpuCore().Scheduler();
return CurrentCpuCore().Scheduler();
}
const std::shared_ptr<Kernel::Scheduler>& System::Scheduler(std::size_t core_index) {
ASSERT(core_index < NUM_CPU_CORES);
return impl->cpu_cores[core_index]->Scheduler();
const Kernel::Scheduler& System::CurrentScheduler() const {
return CurrentCpuCore().Scheduler();
}
Kernel::Scheduler& System::Scheduler(std::size_t core_index) {
return CpuCore(core_index).Scheduler();
}
const Kernel::Scheduler& System::Scheduler(std::size_t core_index) const {
return CpuCore(core_index).Scheduler();
}
Kernel::Process* System::CurrentProcess() {
@@ -371,8 +391,11 @@ const Kernel::Process* System::CurrentProcess() const {
}
ARM_Interface& System::ArmInterface(std::size_t core_index) {
ASSERT(core_index < NUM_CPU_CORES);
return impl->cpu_cores[core_index]->ArmInterface();
return CpuCore(core_index).ArmInterface();
}
const ARM_Interface& System::ArmInterface(std::size_t core_index) const {
return CpuCore(core_index).ArmInterface();
}
Cpu& System::CpuCore(std::size_t core_index) {
@@ -380,10 +403,19 @@ Cpu& System::CpuCore(std::size_t core_index) {
return *impl->cpu_cores[core_index];
}
const Cpu& System::CpuCore(std::size_t core_index) const {
ASSERT(core_index < NUM_CPU_CORES);
return *impl->cpu_cores[core_index];
}
ExclusiveMonitor& System::Monitor() {
return *impl->cpu_exclusive_monitor;
}
const ExclusiveMonitor& System::Monitor() const {
return *impl->cpu_exclusive_monitor;
}
Tegra::GPU& System::GPU() {
return *impl->gpu_core;
}

View File

@@ -129,11 +129,11 @@ public:
*/
bool IsPoweredOn() const;
/**
* Returns a reference to the telemetry session for this emulation session.
* @returns Reference to the telemetry session.
*/
Core::TelemetrySession& TelemetrySession() const;
/// Gets a reference to the telemetry session for this emulation session.
Core::TelemetrySession& TelemetrySession();
/// Gets a reference to the telemetry session for this emulation session.
const Core::TelemetrySession& TelemetrySession() const;
/// Prepare the core emulation for a reschedule
void PrepareReschedule();
@@ -144,21 +144,36 @@ public:
/// Gets an ARM interface to the CPU core that is currently running
ARM_Interface& CurrentArmInterface();
/// Gets an ARM interface to the CPU core that is currently running
const ARM_Interface& CurrentArmInterface() const;
/// Gets the index of the currently running CPU core
std::size_t CurrentCoreIndex();
std::size_t CurrentCoreIndex() const;
/// Gets the scheduler for the CPU core that is currently running
Kernel::Scheduler& CurrentScheduler();
/// Gets an ARM interface to the CPU core with the specified index
/// Gets the scheduler for the CPU core that is currently running
const Kernel::Scheduler& CurrentScheduler() const;
/// Gets a reference to an ARM interface for the CPU core with the specified index
ARM_Interface& ArmInterface(std::size_t core_index);
/// Gets a const reference to an ARM interface from the CPU core with the specified index
const ARM_Interface& ArmInterface(std::size_t core_index) const;
/// Gets a CPU interface to the CPU core with the specified index
Cpu& CpuCore(std::size_t core_index);
/// Gets the exclusive monitor
/// Gets a CPU interface to the CPU core with the specified index
const Cpu& CpuCore(std::size_t core_index) const;
/// Gets a reference to the exclusive monitor
ExclusiveMonitor& Monitor();
/// Gets a constant reference to the exclusive monitor
const ExclusiveMonitor& Monitor() const;
/// Gets a mutable reference to the GPU interface
Tegra::GPU& GPU();
@@ -172,7 +187,10 @@ public:
const VideoCore::RendererBase& Renderer() const;
/// Gets the scheduler for the CPU core with the specified index
const std::shared_ptr<Kernel::Scheduler>& Scheduler(std::size_t core_index);
Kernel::Scheduler& Scheduler(std::size_t core_index);
/// Gets the scheduler for the CPU core with the specified index
const Kernel::Scheduler& Scheduler(std::size_t core_index) const;
/// Provides a pointer to the current process
Kernel::Process* CurrentProcess();
@@ -224,6 +242,9 @@ private:
/// Returns the currently running CPU core
Cpu& CurrentCpuCore();
/// Returns the currently running CPU core
const Cpu& CurrentCpuCore() const;
/**
* Initialize the emulated system.
* @param emu_window Reference to the host-system window used for video output and keyboard

View File

@@ -49,10 +49,8 @@ bool CpuBarrier::Rendezvous() {
return false;
}
Cpu::Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
std::shared_ptr<CpuBarrier> cpu_barrier, std::size_t core_index)
: cpu_barrier{std::move(cpu_barrier)}, core_index{core_index} {
Cpu::Cpu(ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier, std::size_t core_index)
: cpu_barrier{cpu_barrier}, core_index{core_index} {
if (Settings::values.use_cpu_jit) {
#ifdef ARCHITECTURE_x86_64
arm_interface = std::make_unique<ARM_Dynarmic>(exclusive_monitor, core_index);
@@ -64,15 +62,15 @@ Cpu::Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
arm_interface = std::make_unique<ARM_Unicorn>();
}
scheduler = std::make_shared<Kernel::Scheduler>(*arm_interface);
scheduler = std::make_unique<Kernel::Scheduler>(*arm_interface);
}
Cpu::~Cpu() = default;
std::shared_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_cores) {
std::unique_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_cores) {
if (Settings::values.use_cpu_jit) {
#ifdef ARCHITECTURE_x86_64
return std::make_shared<DynarmicExclusiveMonitor>(num_cores);
return std::make_unique<DynarmicExclusiveMonitor>(num_cores);
#else
return nullptr; // TODO(merry): Passthrough exclusive monitor
#endif
@@ -83,7 +81,7 @@ std::shared_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_core
void Cpu::RunLoop(bool tight_loop) {
// Wait for all other CPU cores to complete the previous slice, such that they run in lock-step
if (!cpu_barrier->Rendezvous()) {
if (!cpu_barrier.Rendezvous()) {
// If rendezvous failed, session has been killed
return;
}

View File

@@ -41,8 +41,7 @@ private:
class Cpu {
public:
Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor,
std::shared_ptr<CpuBarrier> cpu_barrier, std::size_t core_index);
Cpu(ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier, std::size_t core_index);
~Cpu();
void RunLoop(bool tight_loop = true);
@@ -59,8 +58,12 @@ public:
return *arm_interface;
}
const std::shared_ptr<Kernel::Scheduler>& Scheduler() const {
return scheduler;
Kernel::Scheduler& Scheduler() {
return *scheduler;
}
const Kernel::Scheduler& Scheduler() const {
return *scheduler;
}
bool IsMainCore() const {
@@ -71,14 +74,14 @@ public:
return core_index;
}
static std::shared_ptr<ExclusiveMonitor> MakeExclusiveMonitor(std::size_t num_cores);
static std::unique_ptr<ExclusiveMonitor> MakeExclusiveMonitor(std::size_t num_cores);
private:
void Reschedule();
std::unique_ptr<ARM_Interface> arm_interface;
std::shared_ptr<CpuBarrier> cpu_barrier;
std::shared_ptr<Kernel::Scheduler> scheduler;
CpuBarrier& cpu_barrier;
std::unique_ptr<Kernel::Scheduler> scheduler;
std::atomic<bool> reschedule_pending = false;
std::size_t core_index;

View File

@@ -4,23 +4,56 @@
#include <algorithm>
#include <array>
#include <bitset>
#include <cctype>
#include <fstream>
#include <locale>
#include <map>
#include <sstream>
#include <string_view>
#include <tuple>
#include <vector>
#include <mbedtls/bignum.h>
#include <mbedtls/cipher.h>
#include <mbedtls/cmac.h>
#include <mbedtls/sha256.h>
#include "common/common_funcs.h"
#include "common/common_paths.h"
#include "common/file_util.h"
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "core/crypto/aes_util.h"
#include "core/crypto/key_manager.h"
#include "core/crypto/partition_data_manager.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/partition_filesystem.h"
#include "core/file_sys/registered_cache.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h"
#include "core/settings.h"
namespace Core::Crypto {
constexpr u64 CURRENT_CRYPTO_REVISION = 0x5;
using namespace Common;
const std::array<SHA256Hash, 2> eticket_source_hashes{
"B71DB271DC338DF380AA2C4335EF8873B1AFD408E80B3582D8719FC81C5E511C"_array32, // eticket_rsa_kek_source
"E8965A187D30E57869F562D04383C996DE487BBA5761363D2D4D32391866A85C"_array32, // eticket_rsa_kekek_source
};
const std::map<std::pair<S128KeyType, u64>, std::string> KEYS_VARIABLE_LENGTH{
{{S128KeyType::Master, 0}, "master_key_"},
{{S128KeyType::Package1, 0}, "package1_key_"},
{{S128KeyType::Package2, 0}, "package2_key_"},
{{S128KeyType::Titlekek, 0}, "titlekek_"},
{{S128KeyType::Source, static_cast<u64>(SourceKeyType::Keyblob)}, "keyblob_key_source_"},
{{S128KeyType::Keyblob, 0}, "keyblob_key_"},
{{S128KeyType::KeyblobMAC, 0}, "keyblob_mac_key_"},
};
Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed) {
Key128 out{};
@@ -37,57 +70,136 @@ Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, K
return out;
}
boost::optional<Key128> DeriveSDSeed() {
Key128 DeriveKeyblobKey(const Key128& sbk, const Key128& tsec, Key128 source) {
AESCipher<Key128> sbk_cipher(sbk, Mode::ECB);
AESCipher<Key128> tsec_cipher(tsec, Mode::ECB);
tsec_cipher.Transcode(source.data(), source.size(), source.data(), Op::Decrypt);
sbk_cipher.Transcode(source.data(), source.size(), source.data(), Op::Decrypt);
return source;
}
Key128 DeriveMasterKey(const std::array<u8, 0x90>& keyblob, const Key128& master_source) {
Key128 master_root;
std::memcpy(master_root.data(), keyblob.data(), sizeof(Key128));
AESCipher<Key128> master_cipher(master_root, Mode::ECB);
Key128 master{};
master_cipher.Transcode(master_source.data(), master_source.size(), master.data(), Op::Decrypt);
return master;
}
std::array<u8, 144> DecryptKeyblob(const std::array<u8, 176>& encrypted_keyblob,
const Key128& key) {
std::array<u8, 0x90> keyblob;
AESCipher<Key128> cipher(key, Mode::CTR);
cipher.SetIV(std::vector<u8>(encrypted_keyblob.data() + 0x10, encrypted_keyblob.data() + 0x20));
cipher.Transcode(encrypted_keyblob.data() + 0x20, keyblob.size(), keyblob.data(), Op::Decrypt);
return keyblob;
}
void KeyManager::DeriveGeneralPurposeKeys(std::size_t crypto_revision) {
const auto kek_generation_source =
GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration));
const auto key_generation_source =
GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration));
if (HasKey(S128KeyType::Master, crypto_revision)) {
for (auto kak_type :
{KeyAreaKeyType::Application, KeyAreaKeyType::Ocean, KeyAreaKeyType::System}) {
if (HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
static_cast<u64>(kak_type))) {
const auto source =
GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
static_cast<u64>(kak_type));
const auto kek =
GenerateKeyEncryptionKey(source, GetKey(S128KeyType::Master, crypto_revision),
kek_generation_source, key_generation_source);
SetKey(S128KeyType::KeyArea, kek, crypto_revision, static_cast<u64>(kak_type));
}
}
AESCipher<Key128> master_cipher(GetKey(S128KeyType::Master, crypto_revision), Mode::ECB);
for (auto key_type : {SourceKeyType::Titlekek, SourceKeyType::Package2}) {
if (HasKey(S128KeyType::Source, static_cast<u64>(key_type))) {
Key128 key{};
master_cipher.Transcode(
GetKey(S128KeyType::Source, static_cast<u64>(key_type)).data(), key.size(),
key.data(), Op::Decrypt);
SetKey(key_type == SourceKeyType::Titlekek ? S128KeyType::Titlekek
: S128KeyType::Package2,
key, crypto_revision);
}
}
}
}
Key128 DeriveKeyblobMACKey(const Key128& keyblob_key, const Key128& mac_source) {
AESCipher<Key128> mac_cipher(keyblob_key, Mode::ECB);
Key128 mac_key{};
mac_cipher.Transcode(mac_source.data(), mac_key.size(), mac_key.data(), Op::Decrypt);
return mac_key;
}
std::optional<Key128> DeriveSDSeed() {
const FileUtil::IOFile save_43(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
"/system/save/8000000000000043",
"rb+");
if (!save_43.IsOpen())
return boost::none;
return {};
const FileUtil::IOFile sd_private(
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) + "/Nintendo/Contents/private", "rb+");
if (!sd_private.IsOpen())
return boost::none;
return {};
sd_private.Seek(0, SEEK_SET);
std::array<u8, 0x10> private_seed{};
if (sd_private.ReadBytes(private_seed.data(), private_seed.size()) != 0x10)
return boost::none;
if (sd_private.ReadBytes(private_seed.data(), private_seed.size()) != private_seed.size()) {
return {};
}
std::array<u8, 0x10> buffer{};
std::size_t offset = 0;
for (; offset + 0x10 < save_43.GetSize(); ++offset) {
save_43.Seek(offset, SEEK_SET);
if (!save_43.Seek(offset, SEEK_SET)) {
return {};
}
save_43.ReadBytes(buffer.data(), buffer.size());
if (buffer == private_seed)
if (buffer == private_seed) {
break;
}
}
if (offset + 0x10 >= save_43.GetSize())
return boost::none;
if (!save_43.Seek(offset + 0x10, SEEK_SET)) {
return {};
}
Key128 seed{};
save_43.Seek(offset + 0x10, SEEK_SET);
save_43.ReadBytes(seed.data(), seed.size());
if (save_43.ReadBytes(seed.data(), seed.size()) != seed.size()) {
return {};
}
return seed;
}
Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, const KeyManager& keys) {
if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKEK)))
Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys) {
if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek)))
return Loader::ResultStatus::ErrorMissingSDKEKSource;
if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKEKGeneration)))
if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration)))
return Loader::ResultStatus::ErrorMissingAESKEKGenerationSource;
if (!keys.HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration)))
return Loader::ResultStatus::ErrorMissingAESKeyGenerationSource;
const auto sd_kek_source =
keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKEK));
keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek));
const auto aes_kek_gen =
keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKEKGeneration));
keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration));
const auto aes_key_gen =
keys.GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration));
const auto master_00 = keys.GetKey(S128KeyType::Master);
const auto sd_kek =
GenerateKeyEncryptionKey(sd_kek_source, master_00, aes_kek_gen, aes_key_gen);
keys.SetKey(S128KeyType::SDKek, sd_kek);
if (!keys.HasKey(S128KeyType::SDSeed))
return Loader::ResultStatus::ErrorMissingSDSeed;
@@ -118,9 +230,147 @@ Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, const KeyManag
return source; ///< Return unaltered source to satisfy output requirement.
});
keys.SetKey(S256KeyType::SDKey, sd_keys[0], static_cast<u64>(SDKeyType::Save));
keys.SetKey(S256KeyType::SDKey, sd_keys[1], static_cast<u64>(SDKeyType::NCA));
return Loader::ResultStatus::Success;
}
std::vector<TicketRaw> GetTicketblob(const FileUtil::IOFile& ticket_save) {
if (!ticket_save.IsOpen())
return {};
std::vector<u8> buffer(ticket_save.GetSize());
if (ticket_save.ReadBytes(buffer.data(), buffer.size()) != buffer.size()) {
return {};
}
std::vector<TicketRaw> out;
u32 magic{};
for (std::size_t offset = 0; offset + 0x4 < buffer.size(); ++offset) {
if (buffer[offset] == 0x4 && buffer[offset + 1] == 0x0 && buffer[offset + 2] == 0x1 &&
buffer[offset + 3] == 0x0) {
out.emplace_back();
auto& next = out.back();
std::memcpy(&next, buffer.data() + offset, sizeof(TicketRaw));
offset += next.size();
}
}
return out;
}
template <size_t size>
static std::array<u8, size> operator^(const std::array<u8, size>& lhs,
const std::array<u8, size>& rhs) {
std::array<u8, size> out{};
std::transform(lhs.begin(), lhs.end(), rhs.begin(), out.begin(), std::bit_xor<>());
return out;
}
template <size_t target_size, size_t in_size>
static std::array<u8, target_size> MGF1(const std::array<u8, in_size>& seed) {
// Avoids truncation overflow within the loop below.
static_assert(target_size <= 0xFF);
std::array<u8, in_size + 4> seed_exp{};
std::memcpy(seed_exp.data(), seed.data(), in_size);
std::vector<u8> out;
size_t i = 0;
while (out.size() < target_size) {
out.resize(out.size() + 0x20);
seed_exp[in_size + 3] = static_cast<u8>(i);
mbedtls_sha256(seed_exp.data(), seed_exp.size(), out.data() + out.size() - 0x20, 0);
++i;
}
std::array<u8, target_size> target;
std::memcpy(target.data(), out.data(), target_size);
return target;
}
template <size_t size>
static std::optional<u64> FindTicketOffset(const std::array<u8, size>& data) {
u64 offset = 0;
for (size_t i = 0x20; i < data.size() - 0x10; ++i) {
if (data[i] == 0x1) {
offset = i + 1;
break;
} else if (data[i] != 0x0) {
return {};
}
}
return offset;
}
std::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket,
const RSAKeyPair<2048>& key) {
u32 cert_authority;
std::memcpy(&cert_authority, ticket.data() + 0x140, sizeof(cert_authority));
if (cert_authority == 0)
return {};
if (cert_authority != Common::MakeMagic('R', 'o', 'o', 't')) {
LOG_INFO(Crypto,
"Attempting to parse ticket with non-standard certificate authority {:08X}.",
cert_authority);
}
Key128 rights_id;
std::memcpy(rights_id.data(), ticket.data() + 0x2A0, sizeof(Key128));
if (rights_id == Key128{})
return {};
Key128 key_temp{};
if (!std::any_of(ticket.begin() + 0x190, ticket.begin() + 0x280, [](u8 b) { return b != 0; })) {
std::memcpy(key_temp.data(), ticket.data() + 0x180, key_temp.size());
return std::make_pair(rights_id, key_temp);
}
mbedtls_mpi D; // RSA Private Exponent
mbedtls_mpi N; // RSA Modulus
mbedtls_mpi S; // Input
mbedtls_mpi M; // Output
mbedtls_mpi_init(&D);
mbedtls_mpi_init(&N);
mbedtls_mpi_init(&S);
mbedtls_mpi_init(&M);
mbedtls_mpi_read_binary(&D, key.decryption_key.data(), key.decryption_key.size());
mbedtls_mpi_read_binary(&N, key.modulus.data(), key.modulus.size());
mbedtls_mpi_read_binary(&S, ticket.data() + 0x180, 0x100);
mbedtls_mpi_exp_mod(&M, &S, &D, &N, nullptr);
std::array<u8, 0x100> rsa_step;
mbedtls_mpi_write_binary(&M, rsa_step.data(), rsa_step.size());
u8 m_0 = rsa_step[0];
std::array<u8, 0x20> m_1;
std::memcpy(m_1.data(), rsa_step.data() + 0x01, m_1.size());
std::array<u8, 0xDF> m_2;
std::memcpy(m_2.data(), rsa_step.data() + 0x21, m_2.size());
if (m_0 != 0)
return {};
m_1 = m_1 ^ MGF1<0x20>(m_2);
m_2 = m_2 ^ MGF1<0xDF>(m_1);
const auto offset = FindTicketOffset(m_2);
if (!offset)
return {};
ASSERT(*offset > 0);
std::memcpy(key_temp.data(), m_2.data() + *offset, key_temp.size());
return std::make_pair(rights_id, key_temp);
}
KeyManager::KeyManager() {
// Initialize keys
const std::string hactool_keys_dir = FileUtil::GetHactoolConfigurationPath();
@@ -137,6 +387,15 @@ KeyManager::KeyManager() {
AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "title.keys", true);
AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, "title.keys_autogenerated", true);
AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "console.keys", false);
AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, "console.keys_autogenerated", false);
}
static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_t length) {
if (base.size() < begin + length)
return false;
return std::all_of(base.begin() + begin, base.begin() + begin + length,
[](u8 c) { return std::isxdigit(c); });
}
void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
@@ -158,6 +417,9 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
out[0].erase(std::remove(out[0].begin(), out[0].end(), ' '), out[0].end());
out[1].erase(std::remove(out[1].begin(), out[1].end(), ' '), out[1].end());
if (out[0].compare(0, 1, "#") == 0)
continue;
if (is_title_keys) {
auto rights_id_raw = Common::HexStringToArray<16>(out[0]);
u128 rights_id{};
@@ -174,6 +436,50 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
const auto index = s256_file_id.at(out[0]);
Key256 key = Common::HexStringToArray<32>(out[1]);
s256_keys[{index.type, index.field1, index.field2}] = key;
} else if (out[0].compare(0, 8, "keyblob_") == 0 &&
out[0].compare(0, 9, "keyblob_k") != 0) {
if (!ValidCryptoRevisionString(out[0], 8, 2))
continue;
const auto index = std::stoul(out[0].substr(8, 2), nullptr, 16);
keyblobs[index] = Common::HexStringToArray<0x90>(out[1]);
} else if (out[0].compare(0, 18, "encrypted_keyblob_") == 0) {
if (!ValidCryptoRevisionString(out[0], 18, 2))
continue;
const auto index = std::stoul(out[0].substr(18, 2), nullptr, 16);
encrypted_keyblobs[index] = Common::HexStringToArray<0xB0>(out[1]);
} else {
for (const auto& kv : KEYS_VARIABLE_LENGTH) {
if (!ValidCryptoRevisionString(out[0], kv.second.size(), 2))
continue;
if (out[0].compare(0, kv.second.size(), kv.second) == 0) {
const auto index =
std::stoul(out[0].substr(kv.second.size(), 2), nullptr, 16);
const auto sub = kv.first.second;
if (sub == 0) {
s128_keys[{kv.first.first, index, 0}] =
Common::HexStringToArray<16>(out[1]);
} else {
s128_keys[{kv.first.first, kv.first.second, index}] =
Common::HexStringToArray<16>(out[1]);
}
break;
}
}
static constexpr std::array<const char*, 3> kak_names = {
"key_area_key_application_", "key_area_key_ocean_", "key_area_key_system_"};
for (size_t j = 0; j < kak_names.size(); ++j) {
const auto& match = kak_names[j];
if (out[0].compare(0, std::strlen(match), match) == 0) {
const auto index =
std::stoul(out[0].substr(std::strlen(match), 2), nullptr, 16);
s128_keys[{S128KeyType::KeyArea, index, j}] =
Common::HexStringToArray<16>(out[1]);
}
}
}
}
}
@@ -187,6 +493,28 @@ void KeyManager::AttemptLoadKeyFile(const std::string& dir1, const std::string&
LoadFromFile(dir2 + DIR_SEP + filename, title);
}
bool KeyManager::BaseDeriveNecessary() const {
const auto check_key_existence = [this](auto key_type, u64 index1 = 0, u64 index2 = 0) {
return !HasKey(key_type, index1, index2);
};
if (check_key_existence(S256KeyType::Header))
return true;
for (size_t i = 0; i < CURRENT_CRYPTO_REVISION; ++i) {
if (check_key_existence(S128KeyType::Master, i) ||
check_key_existence(S128KeyType::KeyArea, i,
static_cast<u64>(KeyAreaKeyType::Application)) ||
check_key_existence(S128KeyType::KeyArea, i, static_cast<u64>(KeyAreaKeyType::Ocean)) ||
check_key_existence(S128KeyType::KeyArea, i,
static_cast<u64>(KeyAreaKeyType::System)) ||
check_key_existence(S128KeyType::Titlekek, i))
return true;
}
return false;
}
bool KeyManager::HasKey(S128KeyType id, u64 field1, u64 field2) const {
return s128_keys.find({id, field1, field2}) != s128_keys.end();
}
@@ -207,13 +535,30 @@ Key256 KeyManager::GetKey(S256KeyType id, u64 field1, u64 field2) const {
return s256_keys.at({id, field1, field2});
}
template <std::size_t Size>
void KeyManager::WriteKeyToFile(bool title_key, std::string_view keyname,
Key256 KeyManager::GetBISKey(u8 partition_id) const {
Key256 out{};
for (const auto& bis_type : {BISKeyType::Crypto, BISKeyType::Tweak}) {
if (HasKey(S128KeyType::BIS, partition_id, static_cast<u64>(bis_type))) {
std::memcpy(
out.data() + sizeof(Key128) * static_cast<u64>(bis_type),
s128_keys.at({S128KeyType::BIS, partition_id, static_cast<u64>(bis_type)}).data(),
sizeof(Key128));
}
}
return out;
}
template <size_t Size>
void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname,
const std::array<u8, Size>& key) {
const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir);
std::string filename = "title.keys_autogenerated";
if (!title_key)
if (category == KeyCategory::Standard)
filename = dev_mode ? "dev.keys_autogenerated" : "prod.keys_autogenerated";
else if (category == KeyCategory::Console)
filename = "console.keys_autogenerated";
const auto add_info_text = !FileUtil::Exists(yuzu_keys_dir + DIR_SEP + filename);
FileUtil::CreateFullPath(yuzu_keys_dir + DIR_SEP + filename);
std::ofstream file(yuzu_keys_dir + DIR_SEP + filename, std::ios::app);
@@ -227,7 +572,7 @@ void KeyManager::WriteKeyToFile(bool title_key, std::string_view keyname,
}
file << fmt::format("\n{} = {}", keyname, Common::HexArrayToString(key));
AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, filename, title_key);
AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, filename, category == KeyCategory::Title);
}
void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
@@ -237,8 +582,15 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
Key128 rights_id;
std::memcpy(rights_id.data(), &field2, sizeof(u64));
std::memcpy(rights_id.data() + sizeof(u64), &field1, sizeof(u64));
WriteKeyToFile(true, Common::HexArrayToString(rights_id), key);
WriteKeyToFile(KeyCategory::Title, Common::HexArrayToString(rights_id), key);
}
auto category = KeyCategory::Standard;
if (id == S128KeyType::Keyblob || id == S128KeyType::KeyblobMAC || id == S128KeyType::TSEC ||
id == S128KeyType::SecureBoot || id == S128KeyType::SDSeed || id == S128KeyType::BIS) {
category = KeyCategory::Console;
}
const auto iter2 = std::find_if(
s128_file_id.begin(), s128_file_id.end(),
[&id, &field1, &field2](const std::pair<std::string, KeyIndex<S128KeyType>> elem) {
@@ -246,7 +598,30 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
std::tie(id, field1, field2);
});
if (iter2 != s128_file_id.end())
WriteKeyToFile(false, iter2->first, key);
WriteKeyToFile(category, iter2->first, key);
// Variable cases
if (id == S128KeyType::KeyArea) {
static constexpr std::array<const char*, 3> kak_names = {"key_area_key_application_{:02X}",
"key_area_key_ocean_{:02X}",
"key_area_key_system_{:02X}"};
WriteKeyToFile(category, fmt::format(kak_names.at(field2), field1), key);
} else if (id == S128KeyType::Master) {
WriteKeyToFile(category, fmt::format("master_key_{:02X}", field1), key);
} else if (id == S128KeyType::Package1) {
WriteKeyToFile(category, fmt::format("package1_key_{:02X}", field1), key);
} else if (id == S128KeyType::Package2) {
WriteKeyToFile(category, fmt::format("package2_key_{:02X}", field1), key);
} else if (id == S128KeyType::Titlekek) {
WriteKeyToFile(category, fmt::format("titlekek_{:02X}", field1), key);
} else if (id == S128KeyType::Keyblob) {
WriteKeyToFile(category, fmt::format("keyblob_key_{:02X}", field1), key);
} else if (id == S128KeyType::KeyblobMAC) {
WriteKeyToFile(category, fmt::format("keyblob_mac_key_{:02X}", field1), key);
} else if (id == S128KeyType::Source && field1 == static_cast<u64>(SourceKeyType::Keyblob)) {
WriteKeyToFile(category, fmt::format("keyblob_key_source_{:02X}", field2), key);
}
s128_keys[{id, field1, field2}] = key;
}
@@ -260,7 +635,7 @@ void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
std::tie(id, field1, field2);
});
if (iter != s256_file_id.end())
WriteKeyToFile(false, iter->first, key);
WriteKeyToFile(KeyCategory::Standard, iter->first, key);
s256_keys[{id, field1, field2}] = key;
}
@@ -286,63 +661,391 @@ void KeyManager::DeriveSDSeedLazy() {
return;
const auto res = DeriveSDSeed();
if (res != boost::none)
SetKey(S128KeyType::SDSeed, res.get());
if (res)
SetKey(S128KeyType::SDSeed, *res);
}
static Key128 CalculateCMAC(const u8* source, size_t size, const Key128& key) {
Key128 out{};
mbedtls_cipher_cmac(mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_128_ECB), key.data(),
key.size() * 8, source, size, out.data());
return out;
}
void KeyManager::DeriveBase() {
if (!BaseDeriveNecessary())
return;
if (!HasKey(S128KeyType::SecureBoot) || !HasKey(S128KeyType::TSEC))
return;
const auto has_bis = [this](u64 id) {
return HasKey(S128KeyType::BIS, id, static_cast<u64>(BISKeyType::Crypto)) &&
HasKey(S128KeyType::BIS, id, static_cast<u64>(BISKeyType::Tweak));
};
const auto copy_bis = [this](u64 id_from, u64 id_to) {
SetKey(S128KeyType::BIS,
GetKey(S128KeyType::BIS, id_from, static_cast<u64>(BISKeyType::Crypto)), id_to,
static_cast<u64>(BISKeyType::Crypto));
SetKey(S128KeyType::BIS,
GetKey(S128KeyType::BIS, id_from, static_cast<u64>(BISKeyType::Tweak)), id_to,
static_cast<u64>(BISKeyType::Tweak));
};
if (has_bis(2) && !has_bis(3))
copy_bis(2, 3);
else if (has_bis(3) && !has_bis(2))
copy_bis(3, 2);
std::bitset<32> revisions(0xFFFFFFFF);
for (size_t i = 0; i < revisions.size(); ++i) {
if (!HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::Keyblob), i) ||
encrypted_keyblobs[i] == std::array<u8, 0xB0>{}) {
revisions.reset(i);
}
}
if (!revisions.any())
return;
const auto sbk = GetKey(S128KeyType::SecureBoot);
const auto tsec = GetKey(S128KeyType::TSEC);
for (size_t i = 0; i < revisions.size(); ++i) {
if (!revisions[i])
continue;
// Derive keyblob key
const auto key = DeriveKeyblobKey(
sbk, tsec, GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::Keyblob), i));
SetKey(S128KeyType::Keyblob, key, i);
// Derive keyblob MAC key
if (!HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC)))
continue;
const auto mac_key = DeriveKeyblobMACKey(
key, GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC)));
SetKey(S128KeyType::KeyblobMAC, mac_key, i);
Key128 cmac = CalculateCMAC(encrypted_keyblobs[i].data() + 0x10, 0xA0, mac_key);
if (std::memcmp(cmac.data(), encrypted_keyblobs[i].data(), cmac.size()) != 0)
continue;
// Decrypt keyblob
if (keyblobs[i] == std::array<u8, 0x90>{}) {
keyblobs[i] = DecryptKeyblob(encrypted_keyblobs[i], key);
WriteKeyToFile<0x90>(KeyCategory::Console, fmt::format("keyblob_{:02X}", i),
keyblobs[i]);
}
Key128 package1;
std::memcpy(package1.data(), keyblobs[i].data() + 0x80, sizeof(Key128));
SetKey(S128KeyType::Package1, package1, i);
// Derive master key
if (HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::Master))) {
SetKey(S128KeyType::Master,
DeriveMasterKey(keyblobs[i], GetKey(S128KeyType::Source,
static_cast<u64>(SourceKeyType::Master))),
i);
}
}
revisions.set();
for (size_t i = 0; i < revisions.size(); ++i) {
if (!HasKey(S128KeyType::Master, i))
revisions.reset(i);
}
if (!revisions.any())
return;
for (size_t i = 0; i < revisions.size(); ++i) {
if (!revisions[i])
continue;
// Derive general purpose keys
DeriveGeneralPurposeKeys(i);
}
if (HasKey(S128KeyType::Master, 0) &&
HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration)) &&
HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration)) &&
HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::HeaderKek)) &&
HasKey(S256KeyType::HeaderSource)) {
const auto header_kek = GenerateKeyEncryptionKey(
GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::HeaderKek)),
GetKey(S128KeyType::Master, 0),
GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration)),
GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration)));
SetKey(S128KeyType::HeaderKek, header_kek);
AESCipher<Key128> header_cipher(header_kek, Mode::ECB);
Key256 out = GetKey(S256KeyType::HeaderSource);
header_cipher.Transcode(out.data(), out.size(), out.data(), Op::Decrypt);
SetKey(S256KeyType::Header, out);
}
}
void KeyManager::DeriveETicket(PartitionDataManager& data) {
// ETicket keys
const auto es = Service::FileSystem::GetUnionContents()->GetEntry(
0x0100000000000033, FileSys::ContentRecordType::Program);
if (es == nullptr)
return;
const auto exefs = es->GetExeFS();
if (exefs == nullptr)
return;
const auto main = exefs->GetFile("main");
if (main == nullptr)
return;
const auto bytes = main->ReadAllBytes();
const auto eticket_kek = FindKeyFromHex16(bytes, eticket_source_hashes[0]);
const auto eticket_kekek = FindKeyFromHex16(bytes, eticket_source_hashes[1]);
const auto seed3 = data.GetRSAKekSeed3();
const auto mask0 = data.GetRSAKekMask0();
if (eticket_kek != Key128{})
SetKey(S128KeyType::Source, eticket_kek, static_cast<size_t>(SourceKeyType::ETicketKek));
if (eticket_kekek != Key128{}) {
SetKey(S128KeyType::Source, eticket_kekek,
static_cast<size_t>(SourceKeyType::ETicketKekek));
}
if (seed3 != Key128{})
SetKey(S128KeyType::RSAKek, seed3, static_cast<size_t>(RSAKekType::Seed3));
if (mask0 != Key128{})
SetKey(S128KeyType::RSAKek, mask0, static_cast<size_t>(RSAKekType::Mask0));
if (eticket_kek == Key128{} || eticket_kekek == Key128{} || seed3 == Key128{} ||
mask0 == Key128{}) {
return;
}
Key128 rsa_oaep_kek{};
std::transform(seed3.begin(), seed3.end(), mask0.begin(), rsa_oaep_kek.begin(),
std::bit_xor<>());
if (rsa_oaep_kek == Key128{})
return;
SetKey(S128KeyType::Source, rsa_oaep_kek,
static_cast<u64>(SourceKeyType::RSAOaepKekGeneration));
Key128 temp_kek{};
Key128 temp_kekek{};
Key128 eticket_final{};
// Derive ETicket RSA Kek
AESCipher<Key128> es_master(GetKey(S128KeyType::Master), Mode::ECB);
es_master.Transcode(rsa_oaep_kek.data(), rsa_oaep_kek.size(), temp_kek.data(), Op::Decrypt);
AESCipher<Key128> es_kekek(temp_kek, Mode::ECB);
es_kekek.Transcode(eticket_kekek.data(), eticket_kekek.size(), temp_kekek.data(), Op::Decrypt);
AESCipher<Key128> es_kek(temp_kekek, Mode::ECB);
es_kek.Transcode(eticket_kek.data(), eticket_kek.size(), eticket_final.data(), Op::Decrypt);
if (eticket_final == Key128{})
return;
SetKey(S128KeyType::ETicketRSAKek, eticket_final);
// Titlekeys
data.DecryptProdInfo(GetBISKey(0));
const auto eticket_extended_kek = data.GetETicketExtendedKek();
std::vector<u8> extended_iv(0x10);
std::memcpy(extended_iv.data(), eticket_extended_kek.data(), extended_iv.size());
std::array<u8, 0x230> extended_dec{};
AESCipher<Key128> rsa_1(eticket_final, Mode::CTR);
rsa_1.SetIV(extended_iv);
rsa_1.Transcode(eticket_extended_kek.data() + 0x10, eticket_extended_kek.size() - 0x10,
extended_dec.data(), Op::Decrypt);
RSAKeyPair<2048> rsa_key{};
std::memcpy(rsa_key.decryption_key.data(), extended_dec.data(), rsa_key.decryption_key.size());
std::memcpy(rsa_key.modulus.data(), extended_dec.data() + 0x100, rsa_key.modulus.size());
std::memcpy(rsa_key.exponent.data(), extended_dec.data() + 0x200, rsa_key.exponent.size());
const FileUtil::IOFile save1(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
"/system/save/80000000000000e1",
"rb+");
const FileUtil::IOFile save2(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
"/system/save/80000000000000e2",
"rb+");
const auto blob2 = GetTicketblob(save2);
auto res = GetTicketblob(save1);
res.insert(res.end(), blob2.begin(), blob2.end());
for (const auto& raw : res) {
const auto pair = ParseTicket(raw, rsa_key);
if (!pair)
continue;
const auto& [rid, key] = *pair;
u128 rights_id;
std::memcpy(rights_id.data(), rid.data(), rid.size());
SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
}
}
void KeyManager::SetKeyWrapped(S128KeyType id, Key128 key, u64 field1, u64 field2) {
if (key == Key128{})
return;
SetKey(id, key, field1, field2);
}
void KeyManager::SetKeyWrapped(S256KeyType id, Key256 key, u64 field1, u64 field2) {
if (key == Key256{})
return;
SetKey(id, key, field1, field2);
}
void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) {
if (!BaseDeriveNecessary())
return;
if (!data.HasBoot0())
return;
for (size_t i = 0; i < encrypted_keyblobs.size(); ++i) {
if (encrypted_keyblobs[i] != std::array<u8, 0xB0>{})
continue;
encrypted_keyblobs[i] = data.GetEncryptedKeyblob(i);
WriteKeyToFile<0xB0>(KeyCategory::Console, fmt::format("encrypted_keyblob_{:02X}", i),
encrypted_keyblobs[i]);
}
SetKeyWrapped(S128KeyType::Source, data.GetPackage2KeySource(),
static_cast<u64>(SourceKeyType::Package2));
SetKeyWrapped(S128KeyType::Source, data.GetAESKekGenerationSource(),
static_cast<u64>(SourceKeyType::AESKekGeneration));
SetKeyWrapped(S128KeyType::Source, data.GetTitlekekSource(),
static_cast<u64>(SourceKeyType::Titlekek));
SetKeyWrapped(S128KeyType::Source, data.GetMasterKeySource(),
static_cast<u64>(SourceKeyType::Master));
SetKeyWrapped(S128KeyType::Source, data.GetKeyblobMACKeySource(),
static_cast<u64>(SourceKeyType::KeyblobMAC));
for (size_t i = 0; i < PartitionDataManager::MAX_KEYBLOB_SOURCE_HASH; ++i) {
SetKeyWrapped(S128KeyType::Source, data.GetKeyblobKeySource(i),
static_cast<u64>(SourceKeyType::Keyblob), i);
}
if (data.HasFuses())
SetKeyWrapped(S128KeyType::SecureBoot, data.GetSecureBootKey());
DeriveBase();
Key128 latest_master{};
for (s8 i = 0x1F; i >= 0; --i) {
if (GetKey(S128KeyType::Master, static_cast<u8>(i)) != Key128{}) {
latest_master = GetKey(S128KeyType::Master, static_cast<u8>(i));
break;
}
}
const auto masters = data.GetTZMasterKeys(latest_master);
for (size_t i = 0; i < masters.size(); ++i) {
if (masters[i] != Key128{} && !HasKey(S128KeyType::Master, i))
SetKey(S128KeyType::Master, masters[i], i);
}
DeriveBase();
if (!data.HasPackage2())
return;
std::array<Key128, 0x20> package2_keys{};
for (size_t i = 0; i < package2_keys.size(); ++i) {
if (HasKey(S128KeyType::Package2, i))
package2_keys[i] = GetKey(S128KeyType::Package2, i);
}
data.DecryptPackage2(package2_keys, Package2Type::NormalMain);
SetKeyWrapped(S128KeyType::Source, data.GetKeyAreaKeyApplicationSource(),
static_cast<u64>(SourceKeyType::KeyAreaKey),
static_cast<u64>(KeyAreaKeyType::Application));
SetKeyWrapped(S128KeyType::Source, data.GetKeyAreaKeyOceanSource(),
static_cast<u64>(SourceKeyType::KeyAreaKey),
static_cast<u64>(KeyAreaKeyType::Ocean));
SetKeyWrapped(S128KeyType::Source, data.GetKeyAreaKeySystemSource(),
static_cast<u64>(SourceKeyType::KeyAreaKey),
static_cast<u64>(KeyAreaKeyType::System));
SetKeyWrapped(S128KeyType::Source, data.GetSDKekSource(),
static_cast<u64>(SourceKeyType::SDKek));
SetKeyWrapped(S256KeyType::SDKeySource, data.GetSDSaveKeySource(),
static_cast<u64>(SDKeyType::Save));
SetKeyWrapped(S256KeyType::SDKeySource, data.GetSDNCAKeySource(),
static_cast<u64>(SDKeyType::NCA));
SetKeyWrapped(S128KeyType::Source, data.GetHeaderKekSource(),
static_cast<u64>(SourceKeyType::HeaderKek));
SetKeyWrapped(S256KeyType::HeaderSource, data.GetHeaderKeySource());
SetKeyWrapped(S128KeyType::Source, data.GetAESKeyGenerationSource(),
static_cast<u64>(SourceKeyType::AESKeyGeneration));
DeriveBase();
}
const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> KeyManager::s128_file_id = {
{"master_key_00", {S128KeyType::Master, 0, 0}},
{"master_key_01", {S128KeyType::Master, 1, 0}},
{"master_key_02", {S128KeyType::Master, 2, 0}},
{"master_key_03", {S128KeyType::Master, 3, 0}},
{"master_key_04", {S128KeyType::Master, 4, 0}},
{"package1_key_00", {S128KeyType::Package1, 0, 0}},
{"package1_key_01", {S128KeyType::Package1, 1, 0}},
{"package1_key_02", {S128KeyType::Package1, 2, 0}},
{"package1_key_03", {S128KeyType::Package1, 3, 0}},
{"package1_key_04", {S128KeyType::Package1, 4, 0}},
{"package2_key_00", {S128KeyType::Package2, 0, 0}},
{"package2_key_01", {S128KeyType::Package2, 1, 0}},
{"package2_key_02", {S128KeyType::Package2, 2, 0}},
{"package2_key_03", {S128KeyType::Package2, 3, 0}},
{"package2_key_04", {S128KeyType::Package2, 4, 0}},
{"titlekek_00", {S128KeyType::Titlekek, 0, 0}},
{"titlekek_01", {S128KeyType::Titlekek, 1, 0}},
{"titlekek_02", {S128KeyType::Titlekek, 2, 0}},
{"titlekek_03", {S128KeyType::Titlekek, 3, 0}},
{"titlekek_04", {S128KeyType::Titlekek, 4, 0}},
{"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}},
{"key_area_key_application_00",
{S128KeyType::KeyArea, 0, static_cast<u64>(KeyAreaKeyType::Application)}},
{"key_area_key_application_01",
{S128KeyType::KeyArea, 1, static_cast<u64>(KeyAreaKeyType::Application)}},
{"key_area_key_application_02",
{S128KeyType::KeyArea, 2, static_cast<u64>(KeyAreaKeyType::Application)}},
{"key_area_key_application_03",
{S128KeyType::KeyArea, 3, static_cast<u64>(KeyAreaKeyType::Application)}},
{"key_area_key_application_04",
{S128KeyType::KeyArea, 4, static_cast<u64>(KeyAreaKeyType::Application)}},
{"key_area_key_ocean_00", {S128KeyType::KeyArea, 0, static_cast<u64>(KeyAreaKeyType::Ocean)}},
{"key_area_key_ocean_01", {S128KeyType::KeyArea, 1, static_cast<u64>(KeyAreaKeyType::Ocean)}},
{"key_area_key_ocean_02", {S128KeyType::KeyArea, 2, static_cast<u64>(KeyAreaKeyType::Ocean)}},
{"key_area_key_ocean_03", {S128KeyType::KeyArea, 3, static_cast<u64>(KeyAreaKeyType::Ocean)}},
{"key_area_key_ocean_04", {S128KeyType::KeyArea, 4, static_cast<u64>(KeyAreaKeyType::Ocean)}},
{"key_area_key_system_00", {S128KeyType::KeyArea, 0, static_cast<u64>(KeyAreaKeyType::System)}},
{"key_area_key_system_01", {S128KeyType::KeyArea, 1, static_cast<u64>(KeyAreaKeyType::System)}},
{"key_area_key_system_02", {S128KeyType::KeyArea, 2, static_cast<u64>(KeyAreaKeyType::System)}},
{"key_area_key_system_03", {S128KeyType::KeyArea, 3, static_cast<u64>(KeyAreaKeyType::System)}},
{"key_area_key_system_04", {S128KeyType::KeyArea, 4, static_cast<u64>(KeyAreaKeyType::System)}},
{"sd_card_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKEK), 0}},
{"eticket_rsa_kek_source",
{S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKek), 0}},
{"eticket_rsa_kekek_source",
{S128KeyType::Source, static_cast<u64>(SourceKeyType::ETicketKekek), 0}},
{"rsa_kek_mask_0", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Mask0), 0}},
{"rsa_kek_seed_3", {S128KeyType::RSAKek, static_cast<u64>(RSAKekType::Seed3), 0}},
{"rsa_oaep_kek_generation_source",
{S128KeyType::Source, static_cast<u64>(SourceKeyType::RSAOaepKekGeneration), 0}},
{"sd_card_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::SDKek), 0}},
{"aes_kek_generation_source",
{S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKEKGeneration), 0}},
{S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration), 0}},
{"aes_key_generation_source",
{S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration), 0}},
{"package2_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Package2), 0}},
{"master_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Master), 0}},
{"header_kek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::HeaderKek), 0}},
{"key_area_key_application_source",
{S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
static_cast<u64>(KeyAreaKeyType::Application)}},
{"key_area_key_ocean_source",
{S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
static_cast<u64>(KeyAreaKeyType::Ocean)}},
{"key_area_key_system_source",
{S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
static_cast<u64>(KeyAreaKeyType::System)}},
{"titlekek_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::Titlekek), 0}},
{"keyblob_mac_key_source", {S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC)}},
{"tsec_key", {S128KeyType::TSEC, 0, 0}},
{"secure_boot_key", {S128KeyType::SecureBoot, 0, 0}},
{"sd_seed", {S128KeyType::SDSeed, 0, 0}},
{"bis_key_0_crypt", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Crypto)}},
{"bis_key_0_tweak", {S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Tweak)}},
{"bis_key_1_crypt", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Crypto)}},
{"bis_key_1_tweak", {S128KeyType::BIS, 1, static_cast<u64>(BISKeyType::Tweak)}},
{"bis_key_2_crypt", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Crypto)}},
{"bis_key_2_tweak", {S128KeyType::BIS, 2, static_cast<u64>(BISKeyType::Tweak)}},
{"bis_key_3_crypt", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Crypto)}},
{"bis_key_3_tweak", {S128KeyType::BIS, 3, static_cast<u64>(BISKeyType::Tweak)}},
{"header_kek", {S128KeyType::HeaderKek, 0, 0}},
{"sd_card_kek", {S128KeyType::SDKek, 0, 0}},
};
const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> KeyManager::s256_file_id = {
{"header_key", {S256KeyType::Header, 0, 0}},
{"sd_card_save_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::Save), 0}},
{"sd_card_nca_key_source", {S256KeyType::SDKeySource, static_cast<u64>(SDKeyType::NCA), 0}},
{"header_key_source", {S256KeyType::HeaderSource, 0, 0}},
{"sd_card_save_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::Save), 0}},
{"sd_card_nca_key", {S256KeyType::SDKey, static_cast<u64>(SDKeyType::NCA), 0}},
};
} // namespace Core::Crypto

View File

@@ -5,11 +5,19 @@
#pragma once
#include <array>
#include <map>
#include <optional>
#include <string>
#include <boost/container/flat_map.hpp>
#include <boost/optional.hpp>
#include <fmt/format.h>
#include "common/common_types.h"
#include "core/crypto/partition_data_manager.h"
#include "core/file_sys/vfs_types.h"
namespace FileUtil {
class IOFile;
}
namespace Loader {
enum class ResultStatus : u16;
@@ -22,13 +30,30 @@ constexpr u64 TICKET_FILE_TITLEKEY_OFFSET = 0x180;
using Key128 = std::array<u8, 0x10>;
using Key256 = std::array<u8, 0x20>;
using SHA256Hash = std::array<u8, 0x20>;
using TicketRaw = std::array<u8, 0x400>;
static_assert(sizeof(Key128) == 16, "Key128 must be 128 bytes big.");
static_assert(sizeof(Key256) == 32, "Key128 must be 128 bytes big.");
static_assert(sizeof(Key256) == 32, "Key256 must be 256 bytes big.");
template <size_t bit_size, size_t byte_size = (bit_size >> 3)>
struct RSAKeyPair {
std::array<u8, byte_size> encryption_key;
std::array<u8, byte_size> decryption_key;
std::array<u8, byte_size> modulus;
std::array<u8, 4> exponent;
};
enum class KeyCategory : u8 {
Standard,
Title,
Console,
};
enum class S256KeyType : u64 {
Header, //
SDKeySource, // f1=SDKeyType
SDKey, // f1=SDKeyType
Header, //
SDKeySource, // f1=SDKeyType
HeaderSource, //
};
enum class S128KeyType : u64 {
@@ -41,6 +66,14 @@ enum class S128KeyType : u64 {
SDSeed, //
Titlekey, // f1=rights id LSB f2=rights id MSB
Source, // f1=source type, f2= sub id
Keyblob, // f1=crypto revision
KeyblobMAC, // f1=crypto revision
TSEC, //
SecureBoot, //
BIS, // f1=partition (0-3), f2=type {crypt, tweak}
HeaderKek, //
SDKek, //
RSAKek, //
};
enum class KeyAreaKeyType : u8 {
@@ -50,9 +83,19 @@ enum class KeyAreaKeyType : u8 {
};
enum class SourceKeyType : u8 {
SDKEK,
AESKEKGeneration,
AESKeyGeneration,
SDKek, //
AESKekGeneration, //
AESKeyGeneration, //
RSAOaepKekGeneration, //
Master, //
Keyblob, // f2=crypto revision
KeyAreaKey, // f2=KeyAreaKeyType
Titlekek, //
Package2, //
HeaderKek, //
KeyblobMAC, //
ETicketKek, //
ETicketKekek, //
};
enum class SDKeyType : u8 {
@@ -60,6 +103,16 @@ enum class SDKeyType : u8 {
NCA,
};
enum class BISKeyType : u8 {
Crypto,
Tweak,
};
enum class RSAKekType : u8 {
Mask0,
Seed3,
};
template <typename KeyType>
struct KeyIndex {
KeyType type;
@@ -91,6 +144,8 @@ public:
Key128 GetKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const;
Key256 GetKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const;
Key256 GetBISKey(u8 partition_id) const;
void SetKey(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0);
void SetKey(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0);
@@ -100,23 +155,51 @@ public:
// 8*43 and the private file to exist.
void DeriveSDSeedLazy();
bool BaseDeriveNecessary() const;
void DeriveBase();
void DeriveETicket(PartitionDataManager& data);
void PopulateFromPartitionData(PartitionDataManager& data);
private:
boost::container::flat_map<KeyIndex<S128KeyType>, Key128> s128_keys;
boost::container::flat_map<KeyIndex<S256KeyType>, Key256> s256_keys;
std::map<KeyIndex<S128KeyType>, Key128> s128_keys;
std::map<KeyIndex<S256KeyType>, Key256> s256_keys;
std::array<std::array<u8, 0xB0>, 0x20> encrypted_keyblobs{};
std::array<std::array<u8, 0x90>, 0x20> keyblobs{};
bool dev_mode;
void LoadFromFile(const std::string& filename, bool is_title_keys);
void AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2,
const std::string& filename, bool title);
template <std::size_t Size>
void WriteKeyToFile(bool title_key, std::string_view keyname, const std::array<u8, Size>& key);
template <size_t Size>
void WriteKeyToFile(KeyCategory category, std::string_view keyname,
const std::array<u8, Size>& key);
void DeriveGeneralPurposeKeys(std::size_t crypto_revision);
void SetKeyWrapped(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0);
void SetKeyWrapped(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0);
static const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> s128_file_id;
static const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> s256_file_id;
};
Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed);
boost::optional<Key128> DeriveSDSeed();
Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, const KeyManager& keys);
Key128 DeriveKeyblobKey(const Key128& sbk, const Key128& tsec, Key128 source);
Key128 DeriveKeyblobMACKey(const Key128& keyblob_key, const Key128& mac_source);
Key128 DeriveMasterKey(const std::array<u8, 0x90>& keyblob, const Key128& master_source);
std::array<u8, 0x90> DecryptKeyblob(const std::array<u8, 0xB0>& encrypted_keyblob,
const Key128& key);
std::optional<Key128> DeriveSDSeed();
Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys);
std::vector<TicketRaw> GetTicketblob(const FileUtil::IOFile& ticket_save);
// Returns a pair of {rights_id, titlekey}. Fails if the ticket has no certificate authority (offset
// 0x140-0x144 is zero)
std::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket,
const RSAKeyPair<2048>& eticket_extended_key);
} // namespace Core::Crypto

View File

@@ -0,0 +1,594 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
// NOTE TO FUTURE MAINTAINERS:
// When a new version of switch cryptography is released,
// hash the new keyblob source and master key and add the hashes to
// the arrays below.
#include <algorithm>
#include <array>
#include <cctype>
#include <cstring>
#include <mbedtls/sha256.h>
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "common/string_util.h"
#include "common/swap.h"
#include "core/crypto/key_manager.h"
#include "core/crypto/partition_data_manager.h"
#include "core/crypto/xts_encryption_layer.h"
#include "core/file_sys/vfs.h"
#include "core/file_sys/vfs_offset.h"
using namespace Common;
namespace Core::Crypto {
struct Package2Header {
std::array<u8, 0x100> signature;
Key128 header_ctr;
std::array<Key128, 4> section_ctr;
u32_le magic;
u32_le base_offset;
INSERT_PADDING_BYTES(4);
u8 version_max;
u8 version_min;
INSERT_PADDING_BYTES(2);
std::array<u32_le, 4> section_size;
std::array<u32_le, 4> section_offset;
std::array<SHA256Hash, 4> section_hash;
};
static_assert(sizeof(Package2Header) == 0x200, "Package2Header has incorrect size.");
struct INIHeader {
u32_le magic;
u32_le size;
u32_le process_count;
INSERT_PADDING_BYTES(4);
};
static_assert(sizeof(INIHeader) == 0x10, "INIHeader has incorrect size.");
struct SectionHeader {
u32_le offset;
u32_le size_decompressed;
u32_le size_compressed;
u32_le attribute;
};
static_assert(sizeof(SectionHeader) == 0x10, "SectionHeader has incorrect size.");
struct KIPHeader {
u32_le magic;
std::array<char, 12> name;
u64_le title_id;
u32_le category;
u8 priority;
u8 core;
INSERT_PADDING_BYTES(1);
u8 flags;
std::array<SectionHeader, 6> sections;
std::array<u32, 0x20> capabilities;
};
static_assert(sizeof(KIPHeader) == 0x100, "KIPHeader has incorrect size.");
const std::array<SHA256Hash, 0x10> source_hashes{
"B24BD293259DBC7AC5D63F88E60C59792498E6FC5443402C7FFE87EE8B61A3F0"_array32, // keyblob_mac_key_source
"7944862A3A5C31C6720595EFD302245ABD1B54CCDCF33000557681E65C5664A4"_array32, // master_key_source
"21E2DF100FC9E094DB51B47B9B1D6E94ED379DB8B547955BEF8FE08D8DD35603"_array32, // package2_key_source
"FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"_array32, // aes_kek_generation_source
"FBD10056999EDC7ACDB96098E47E2C3606230270D23281E671F0F389FC5BC585"_array32, // aes_key_generation_source
"C48B619827986C7F4E3081D59DB2B460C84312650E9A8E6B458E53E8CBCA4E87"_array32, // titlekek_source
"04AD66143C726B2A139FB6B21128B46F56C553B2B3887110304298D8D0092D9E"_array32, // key_area_key_application_source
"FD434000C8FF2B26F8E9A9D2D2C12F6BE5773CBB9DC86300E1BD99F8EA33A417"_array32, // key_area_key_ocean_source
"1F17B1FD51AD1C2379B58F152CA4912EC2106441E51722F38700D5937A1162F7"_array32, // key_area_key_system_source
"6B2ED877C2C52334AC51E59ABFA7EC457F4A7D01E46291E9F2EAA45F011D24B7"_array32, // sd_card_kek_source
"D482743563D3EA5DCDC3B74E97C9AC8A342164FA041A1DC80F17F6D31E4BC01C"_array32, // sd_card_save_key_source
"2E751CECF7D93A2B957BD5FFCB082FD038CC2853219DD3092C6DAB9838F5A7CC"_array32, // sd_card_nca_key_source
"1888CAED5551B3EDE01499E87CE0D86827F80820EFB275921055AA4E2ABDFFC2"_array32, // header_kek_source
"8F783E46852DF6BE0BA4E19273C4ADBAEE16380043E1B8C418C4089A8BD64AA6"_array32, // header_key_source
"D1757E52F1AE55FA882EC690BC6F954AC46A83DC22F277F8806BD55577C6EED7"_array32, // rsa_kek_seed3
"FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"_array32, // rsa_kek_mask0
};
const std::array<SHA256Hash, 0x20> keyblob_source_hashes{
"8A06FE274AC491436791FDB388BCDD3AB9943BD4DEF8094418CDAC150FD73786"_array32, // keyblob_key_source_00
"2D5CAEB2521FEF70B47E17D6D0F11F8CE2C1E442A979AD8035832C4E9FBCCC4B"_array32, // keyblob_key_source_01
"61C5005E713BAE780641683AF43E5F5C0E03671117F702F401282847D2FC6064"_array32, // keyblob_key_source_02
"8E9795928E1C4428E1B78F0BE724D7294D6934689C11B190943923B9D5B85903"_array32, // keyblob_key_source_03
"95FA33AF95AFF9D9B61D164655B32710ED8D615D46C7D6CC3CC70481B686B402"_array32, // keyblob_key_source_04
"3F5BE7B3C8B1ABD8C10B4B703D44766BA08730562C172A4FE0D6B866B3E2DB3E"_array32, // keyblob_key_source_05
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_06
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_07
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_08
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_09
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0A
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0B
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0C
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0D
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0E
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0F
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_10
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_11
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_12
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_13
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_14
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_15
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_16
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_17
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_18
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_19
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1A
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1B
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1C
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1D
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1E
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1F
};
const std::array<SHA256Hash, 0x20> master_key_hashes{
"0EE359BE3C864BB0782E1D70A718A0342C551EED28C369754F9C4F691BECF7CA"_array32, // master_key_00
"4FE707B7E4ABDAF727C894AAF13B1351BFE2AC90D875F73B2E20FA94B9CC661E"_array32, // master_key_01
"79277C0237A2252EC3DFAC1F7C359C2B3D121E9DB15BB9AB4C2B4408D2F3AE09"_array32, // master_key_02
"4F36C565D13325F65EE134073C6A578FFCB0008E02D69400836844EAB7432754"_array32, // master_key_03
"75FF1D95D26113550EE6FCC20ACB58E97EDEB3A2FF52543ED5AEC63BDCC3DA50"_array32, // master_key_04
"EBE2BCD6704673EC0F88A187BB2AD9F1CC82B718C389425941BDC194DC46B0DD"_array32, // master_key_05
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_06
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_07
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_08
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_09
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0A
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0B
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0C
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0D
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0E
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0F
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_10
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_11
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_12
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_13
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_14
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_15
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_16
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_17
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_18
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_19
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1A
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1B
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1C
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1D
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1E
"0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1F
};
static std::vector<u8> DecompressBLZ(const std::vector<u8>& in) {
const auto data_size = in.size() - 0xC;
u32 compressed_size{};
u32 init_index{};
u32 additional_size{};
std::memcpy(&compressed_size, in.data() + data_size, sizeof(u32));
std::memcpy(&init_index, in.data() + data_size + 0x4, sizeof(u32));
std::memcpy(&additional_size, in.data() + data_size + 0x8, sizeof(u32));
std::vector<u8> out(in.size() + additional_size);
if (compressed_size == in.size())
std::memcpy(out.data(), in.data() + in.size() - compressed_size, compressed_size);
else
std::memcpy(out.data(), in.data(), compressed_size);
auto index = in.size() - init_index;
auto out_index = out.size();
while (out_index > 0) {
--index;
auto control = in[index];
for (size_t i = 0; i < 8; ++i) {
if ((control & 0x80) > 0) {
ASSERT(index >= 2);
index -= 2;
u64 segment_offset = in[index] | in[index + 1] << 8;
u64 segment_size = ((segment_offset >> 12) & 0xF) + 3;
segment_offset &= 0xFFF;
segment_offset += 3;
if (out_index < segment_size)
segment_size = out_index;
ASSERT(out_index >= segment_size);
out_index -= segment_size;
for (size_t j = 0; j < segment_size; ++j) {
ASSERT(out_index + j + segment_offset < out.size());
out[out_index + j] = out[out_index + j + segment_offset];
}
} else {
ASSERT(out_index >= 1);
--out_index;
--index;
out[out_index] = in[index];
}
control <<= 1;
if (out_index == 0)
return out;
}
}
return out;
}
static u8 CalculateMaxKeyblobSourceHash() {
for (s8 i = 0x1F; i >= 0; --i) {
if (keyblob_source_hashes[i] != SHA256Hash{})
return static_cast<u8>(i + 1);
}
return 0;
}
const u8 PartitionDataManager::MAX_KEYBLOB_SOURCE_HASH = CalculateMaxKeyblobSourceHash();
template <size_t key_size = 0x10>
std::array<u8, key_size> FindKeyFromHex(const std::vector<u8>& binary,
const std::array<u8, 0x20>& hash) {
if (binary.size() < key_size)
return {};
std::array<u8, 0x20> temp{};
for (size_t i = 0; i < binary.size() - key_size; ++i) {
mbedtls_sha256(binary.data() + i, key_size, temp.data(), 0);
if (temp != hash)
continue;
std::array<u8, key_size> out{};
std::memcpy(out.data(), binary.data() + i, key_size);
return out;
}
return {};
}
std::array<u8, 16> FindKeyFromHex16(const std::vector<u8>& binary, std::array<u8, 32> hash) {
return FindKeyFromHex<0x10>(binary, hash);
}
static std::array<Key128, 0x20> FindEncryptedMasterKeyFromHex(const std::vector<u8>& binary,
const Key128& key) {
if (binary.size() < 0x10)
return {};
SHA256Hash temp{};
Key128 dec_temp{};
std::array<Key128, 0x20> out{};
AESCipher<Key128> cipher(key, Mode::ECB);
for (size_t i = 0; i < binary.size() - 0x10; ++i) {
cipher.Transcode(binary.data() + i, dec_temp.size(), dec_temp.data(), Op::Decrypt);
mbedtls_sha256(dec_temp.data(), dec_temp.size(), temp.data(), 0);
for (size_t k = 0; k < out.size(); ++k) {
if (temp == master_key_hashes[k]) {
out[k] = dec_temp;
break;
}
}
}
return out;
}
FileSys::VirtualFile FindFileInDirWithNames(const FileSys::VirtualDir& dir,
const std::string& name) {
auto upper = name;
std::transform(upper.begin(), upper.end(), upper.begin(), [](u8 c) { return std::toupper(c); });
for (const auto& fname : {name, name + ".bin", upper, upper + ".BIN"}) {
if (dir->GetFile(fname) != nullptr)
return dir->GetFile(fname);
}
return nullptr;
}
PartitionDataManager::PartitionDataManager(const FileSys::VirtualDir& sysdata_dir)
: boot0(FindFileInDirWithNames(sysdata_dir, "BOOT0")),
fuses(FindFileInDirWithNames(sysdata_dir, "fuses")),
kfuses(FindFileInDirWithNames(sysdata_dir, "kfuses")),
package2({
FindFileInDirWithNames(sysdata_dir, "BCPKG2-1-Normal-Main"),
FindFileInDirWithNames(sysdata_dir, "BCPKG2-2-Normal-Sub"),
FindFileInDirWithNames(sysdata_dir, "BCPKG2-3-SafeMode-Main"),
FindFileInDirWithNames(sysdata_dir, "BCPKG2-4-SafeMode-Sub"),
FindFileInDirWithNames(sysdata_dir, "BCPKG2-5-Repair-Main"),
FindFileInDirWithNames(sysdata_dir, "BCPKG2-6-Repair-Sub"),
}),
prodinfo(FindFileInDirWithNames(sysdata_dir, "PRODINFO")),
secure_monitor(FindFileInDirWithNames(sysdata_dir, "secmon")),
package1_decrypted(FindFileInDirWithNames(sysdata_dir, "pkg1_decr")),
secure_monitor_bytes(secure_monitor == nullptr ? std::vector<u8>{}
: secure_monitor->ReadAllBytes()),
package1_decrypted_bytes(package1_decrypted == nullptr ? std::vector<u8>{}
: package1_decrypted->ReadAllBytes()) {
}
PartitionDataManager::~PartitionDataManager() = default;
bool PartitionDataManager::HasBoot0() const {
return boot0 != nullptr;
}
FileSys::VirtualFile PartitionDataManager::GetBoot0Raw() const {
return boot0;
}
PartitionDataManager::EncryptedKeyBlob PartitionDataManager::GetEncryptedKeyblob(
std::size_t index) const {
if (HasBoot0() && index < NUM_ENCRYPTED_KEYBLOBS)
return GetEncryptedKeyblobs()[index];
return {};
}
PartitionDataManager::EncryptedKeyBlobs PartitionDataManager::GetEncryptedKeyblobs() const {
if (!HasBoot0())
return {};
EncryptedKeyBlobs out{};
for (size_t i = 0; i < out.size(); ++i)
boot0->Read(out[i].data(), out[i].size(), 0x180000 + i * 0x200);
return out;
}
std::vector<u8> PartitionDataManager::GetSecureMonitor() const {
return secure_monitor_bytes;
}
std::array<u8, 16> PartitionDataManager::GetPackage2KeySource() const {
return FindKeyFromHex(secure_monitor_bytes, source_hashes[2]);
}
std::array<u8, 16> PartitionDataManager::GetAESKekGenerationSource() const {
return FindKeyFromHex(secure_monitor_bytes, source_hashes[3]);
}
std::array<u8, 16> PartitionDataManager::GetTitlekekSource() const {
return FindKeyFromHex(secure_monitor_bytes, source_hashes[5]);
}
std::array<std::array<u8, 16>, 32> PartitionDataManager::GetTZMasterKeys(
std::array<u8, 0x10> master_key) const {
return FindEncryptedMasterKeyFromHex(secure_monitor_bytes, master_key);
}
std::array<u8, 16> PartitionDataManager::GetRSAKekSeed3() const {
return FindKeyFromHex(secure_monitor_bytes, source_hashes[14]);
}
std::array<u8, 16> PartitionDataManager::GetRSAKekMask0() const {
return FindKeyFromHex(secure_monitor_bytes, source_hashes[15]);
}
std::vector<u8> PartitionDataManager::GetPackage1Decrypted() const {
return package1_decrypted_bytes;
}
std::array<u8, 16> PartitionDataManager::GetMasterKeySource() const {
return FindKeyFromHex(package1_decrypted_bytes, source_hashes[1]);
}
std::array<u8, 16> PartitionDataManager::GetKeyblobMACKeySource() const {
return FindKeyFromHex(package1_decrypted_bytes, source_hashes[0]);
}
std::array<u8, 16> PartitionDataManager::GetKeyblobKeySource(std::size_t revision) const {
if (keyblob_source_hashes[revision] == SHA256Hash{}) {
LOG_WARNING(Crypto,
"No keyblob source hash for crypto revision {:02X}! Cannot derive keys...",
revision);
}
return FindKeyFromHex(package1_decrypted_bytes, keyblob_source_hashes[revision]);
}
bool PartitionDataManager::HasFuses() const {
return fuses != nullptr;
}
FileSys::VirtualFile PartitionDataManager::GetFusesRaw() const {
return fuses;
}
std::array<u8, 16> PartitionDataManager::GetSecureBootKey() const {
if (!HasFuses())
return {};
Key128 out{};
fuses->Read(out.data(), out.size(), 0xA4);
return out;
}
bool PartitionDataManager::HasKFuses() const {
return kfuses != nullptr;
}
FileSys::VirtualFile PartitionDataManager::GetKFusesRaw() const {
return kfuses;
}
bool PartitionDataManager::HasPackage2(Package2Type type) const {
return package2.at(static_cast<size_t>(type)) != nullptr;
}
FileSys::VirtualFile PartitionDataManager::GetPackage2Raw(Package2Type type) const {
return package2.at(static_cast<size_t>(type));
}
bool AttemptDecrypt(const std::array<u8, 16>& key, Package2Header& header) {
const std::vector<u8> iv(header.header_ctr.begin(), header.header_ctr.end());
Package2Header temp = header;
AESCipher<Key128> cipher(key, Mode::CTR);
cipher.SetIV(iv);
cipher.Transcode(&temp.header_ctr, sizeof(Package2Header) - 0x100, &temp.header_ctr,
Op::Decrypt);
if (temp.magic == Common::MakeMagic('P', 'K', '2', '1')) {
header = temp;
return true;
}
return false;
}
void PartitionDataManager::DecryptPackage2(const std::array<Key128, 0x20>& package2_keys,
Package2Type type) {
FileSys::VirtualFile file = std::make_shared<FileSys::OffsetVfsFile>(
package2[static_cast<size_t>(type)],
package2[static_cast<size_t>(type)]->GetSize() - 0x4000, 0x4000);
Package2Header header{};
if (file->ReadObject(&header) != sizeof(Package2Header))
return;
std::size_t revision = 0xFF;
if (header.magic != Common::MakeMagic('P', 'K', '2', '1')) {
for (std::size_t i = 0; i < package2_keys.size(); ++i) {
if (AttemptDecrypt(package2_keys[i], header)) {
revision = i;
}
}
}
if (header.magic != Common::MakeMagic('P', 'K', '2', '1'))
return;
const auto a = std::make_shared<FileSys::OffsetVfsFile>(
file, header.section_size[1], header.section_size[0] + sizeof(Package2Header));
auto c = a->ReadAllBytes();
AESCipher<Key128> cipher(package2_keys[revision], Mode::CTR);
cipher.SetIV({header.section_ctr[1].begin(), header.section_ctr[1].end()});
cipher.Transcode(c.data(), c.size(), c.data(), Op::Decrypt);
INIHeader ini;
std::memcpy(&ini, c.data(), sizeof(INIHeader));
if (ini.magic != Common::MakeMagic('I', 'N', 'I', '1'))
return;
u64 offset = sizeof(INIHeader);
for (size_t i = 0; i < ini.process_count; ++i) {
KIPHeader kip;
std::memcpy(&kip, c.data() + offset, sizeof(KIPHeader));
if (kip.magic != Common::MakeMagic('K', 'I', 'P', '1'))
return;
const auto name =
Common::StringFromFixedZeroTerminatedBuffer(kip.name.data(), kip.name.size());
if (name != "FS" && name != "spl") {
offset += sizeof(KIPHeader) + kip.sections[0].size_compressed +
kip.sections[1].size_compressed + kip.sections[2].size_compressed;
continue;
}
const u64 initial_offset = sizeof(KIPHeader) + offset;
const auto text_begin = c.cbegin() + initial_offset;
const auto text_end = text_begin + kip.sections[0].size_compressed;
const std::vector<u8> text = DecompressBLZ({text_begin, text_end});
const auto rodata_end = text_end + kip.sections[1].size_compressed;
const std::vector<u8> rodata = DecompressBLZ({text_end, rodata_end});
const auto data_end = rodata_end + kip.sections[2].size_compressed;
const std::vector<u8> data = DecompressBLZ({rodata_end, data_end});
std::vector<u8> out;
out.reserve(text.size() + rodata.size() + data.size());
out.insert(out.end(), text.begin(), text.end());
out.insert(out.end(), rodata.begin(), rodata.end());
out.insert(out.end(), data.begin(), data.end());
offset += sizeof(KIPHeader) + kip.sections[0].size_compressed +
kip.sections[1].size_compressed + kip.sections[2].size_compressed;
if (name == "FS")
package2_fs[static_cast<size_t>(type)] = std::move(out);
else if (name == "spl")
package2_spl[static_cast<size_t>(type)] = std::move(out);
}
}
const std::vector<u8>& PartitionDataManager::GetPackage2FSDecompressed(Package2Type type) const {
return package2_fs.at(static_cast<size_t>(type));
}
std::array<u8, 16> PartitionDataManager::GetKeyAreaKeyApplicationSource(Package2Type type) const {
return FindKeyFromHex(package2_fs.at(static_cast<size_t>(type)), source_hashes[6]);
}
std::array<u8, 16> PartitionDataManager::GetKeyAreaKeyOceanSource(Package2Type type) const {
return FindKeyFromHex(package2_fs.at(static_cast<size_t>(type)), source_hashes[7]);
}
std::array<u8, 16> PartitionDataManager::GetKeyAreaKeySystemSource(Package2Type type) const {
return FindKeyFromHex(package2_fs.at(static_cast<size_t>(type)), source_hashes[8]);
}
std::array<u8, 16> PartitionDataManager::GetSDKekSource(Package2Type type) const {
return FindKeyFromHex(package2_fs.at(static_cast<size_t>(type)), source_hashes[9]);
}
std::array<u8, 32> PartitionDataManager::GetSDSaveKeySource(Package2Type type) const {
return FindKeyFromHex<0x20>(package2_fs.at(static_cast<size_t>(type)), source_hashes[10]);
}
std::array<u8, 32> PartitionDataManager::GetSDNCAKeySource(Package2Type type) const {
return FindKeyFromHex<0x20>(package2_fs.at(static_cast<size_t>(type)), source_hashes[11]);
}
std::array<u8, 16> PartitionDataManager::GetHeaderKekSource(Package2Type type) const {
return FindKeyFromHex(package2_fs.at(static_cast<size_t>(type)), source_hashes[12]);
}
std::array<u8, 32> PartitionDataManager::GetHeaderKeySource(Package2Type type) const {
return FindKeyFromHex<0x20>(package2_fs.at(static_cast<size_t>(type)), source_hashes[13]);
}
const std::vector<u8>& PartitionDataManager::GetPackage2SPLDecompressed(Package2Type type) const {
return package2_spl.at(static_cast<size_t>(type));
}
std::array<u8, 16> PartitionDataManager::GetAESKeyGenerationSource(Package2Type type) const {
return FindKeyFromHex(package2_spl.at(static_cast<size_t>(type)), source_hashes[4]);
}
bool PartitionDataManager::HasProdInfo() const {
return prodinfo != nullptr;
}
FileSys::VirtualFile PartitionDataManager::GetProdInfoRaw() const {
return prodinfo;
}
void PartitionDataManager::DecryptProdInfo(std::array<u8, 0x20> bis_key) {
if (prodinfo == nullptr)
return;
prodinfo_decrypted = std::make_shared<XTSEncryptionLayer>(prodinfo, bis_key);
}
std::array<u8, 576> PartitionDataManager::GetETicketExtendedKek() const {
std::array<u8, 0x240> out{};
if (prodinfo_decrypted != nullptr)
prodinfo_decrypted->Read(out.data(), out.size(), 0x3890);
return out;
}
} // namespace Core::Crypto

View File

@@ -0,0 +1,109 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <vector>
#include "common/common_types.h"
#include "core/file_sys/vfs_types.h"
namespace Core::Crypto {
enum class Package2Type {
NormalMain,
NormalSub,
SafeModeMain,
SafeModeSub,
RepairMain,
RepairSub,
};
class PartitionDataManager {
public:
static const u8 MAX_KEYBLOB_SOURCE_HASH;
static constexpr std::size_t NUM_ENCRYPTED_KEYBLOBS = 32;
static constexpr std::size_t ENCRYPTED_KEYBLOB_SIZE = 0xB0;
using EncryptedKeyBlob = std::array<u8, ENCRYPTED_KEYBLOB_SIZE>;
using EncryptedKeyBlobs = std::array<EncryptedKeyBlob, NUM_ENCRYPTED_KEYBLOBS>;
explicit PartitionDataManager(const FileSys::VirtualDir& sysdata_dir);
~PartitionDataManager();
// BOOT0
bool HasBoot0() const;
FileSys::VirtualFile GetBoot0Raw() const;
EncryptedKeyBlob GetEncryptedKeyblob(std::size_t index) const;
EncryptedKeyBlobs GetEncryptedKeyblobs() const;
std::vector<u8> GetSecureMonitor() const;
std::array<u8, 0x10> GetPackage2KeySource() const;
std::array<u8, 0x10> GetAESKekGenerationSource() const;
std::array<u8, 0x10> GetTitlekekSource() const;
std::array<std::array<u8, 0x10>, 0x20> GetTZMasterKeys(std::array<u8, 0x10> master_key) const;
std::array<u8, 0x10> GetRSAKekSeed3() const;
std::array<u8, 0x10> GetRSAKekMask0() const;
std::vector<u8> GetPackage1Decrypted() const;
std::array<u8, 0x10> GetMasterKeySource() const;
std::array<u8, 0x10> GetKeyblobMACKeySource() const;
std::array<u8, 0x10> GetKeyblobKeySource(std::size_t revision) const;
// Fuses
bool HasFuses() const;
FileSys::VirtualFile GetFusesRaw() const;
std::array<u8, 0x10> GetSecureBootKey() const;
// K-Fuses
bool HasKFuses() const;
FileSys::VirtualFile GetKFusesRaw() const;
// Package2
bool HasPackage2(Package2Type type = Package2Type::NormalMain) const;
FileSys::VirtualFile GetPackage2Raw(Package2Type type = Package2Type::NormalMain) const;
void DecryptPackage2(const std::array<std::array<u8, 16>, 0x20>& package2_keys,
Package2Type type);
const std::vector<u8>& GetPackage2FSDecompressed(
Package2Type type = Package2Type::NormalMain) const;
std::array<u8, 0x10> GetKeyAreaKeyApplicationSource(
Package2Type type = Package2Type::NormalMain) const;
std::array<u8, 0x10> GetKeyAreaKeyOceanSource(
Package2Type type = Package2Type::NormalMain) const;
std::array<u8, 0x10> GetKeyAreaKeySystemSource(
Package2Type type = Package2Type::NormalMain) const;
std::array<u8, 0x10> GetSDKekSource(Package2Type type = Package2Type::NormalMain) const;
std::array<u8, 0x20> GetSDSaveKeySource(Package2Type type = Package2Type::NormalMain) const;
std::array<u8, 0x20> GetSDNCAKeySource(Package2Type type = Package2Type::NormalMain) const;
std::array<u8, 0x10> GetHeaderKekSource(Package2Type type = Package2Type::NormalMain) const;
std::array<u8, 0x20> GetHeaderKeySource(Package2Type type = Package2Type::NormalMain) const;
const std::vector<u8>& GetPackage2SPLDecompressed(
Package2Type type = Package2Type::NormalMain) const;
std::array<u8, 0x10> GetAESKeyGenerationSource(
Package2Type type = Package2Type::NormalMain) const;
// PRODINFO
bool HasProdInfo() const;
FileSys::VirtualFile GetProdInfoRaw() const;
void DecryptProdInfo(std::array<u8, 0x20> bis_key);
std::array<u8, 0x240> GetETicketExtendedKek() const;
private:
FileSys::VirtualFile boot0;
FileSys::VirtualFile fuses;
FileSys::VirtualFile kfuses;
std::array<FileSys::VirtualFile, 6> package2;
FileSys::VirtualFile prodinfo;
FileSys::VirtualFile secure_monitor;
FileSys::VirtualFile package1_decrypted;
// Processed
std::array<FileSys::VirtualFile, 6> package2_decrypted;
FileSys::VirtualFile prodinfo_decrypted;
std::vector<u8> secure_monitor_bytes;
std::vector<u8> package1_decrypted_bytes;
std::array<std::vector<u8>, 6> package2_fs;
std::array<std::vector<u8>, 6> package2_spl;
};
std::array<u8, 0x10> FindKeyFromHex16(const std::vector<u8>& binary, std::array<u8, 0x20> hash);
} // namespace Core::Crypto

View File

@@ -10,19 +10,19 @@ namespace FileSys {
BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_)
: nand_root(std::move(nand_root_)), load_root(std::move(load_root_)),
sysnand_cache(std::make_shared<RegisteredCache>(
sysnand_cache(std::make_unique<RegisteredCache>(
GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))),
usrnand_cache(std::make_shared<RegisteredCache>(
usrnand_cache(std::make_unique<RegisteredCache>(
GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))) {}
BISFactory::~BISFactory() = default;
std::shared_ptr<RegisteredCache> BISFactory::GetSystemNANDContents() const {
return sysnand_cache;
RegisteredCache* BISFactory::GetSystemNANDContents() const {
return sysnand_cache.get();
}
std::shared_ptr<RegisteredCache> BISFactory::GetUserNANDContents() const {
return usrnand_cache;
RegisteredCache* BISFactory::GetUserNANDContents() const {
return usrnand_cache.get();
}
VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const {

View File

@@ -20,8 +20,8 @@ public:
explicit BISFactory(VirtualDir nand_root, VirtualDir load_root);
~BISFactory();
std::shared_ptr<RegisteredCache> GetSystemNANDContents() const;
std::shared_ptr<RegisteredCache> GetUserNANDContents() const;
RegisteredCache* GetSystemNANDContents() const;
RegisteredCache* GetUserNANDContents() const;
VirtualDir GetModificationLoadRoot(u64 title_id) const;
@@ -29,8 +29,8 @@ private:
VirtualDir nand_root;
VirtualDir load_root;
std::shared_ptr<RegisteredCache> sysnand_cache;
std::shared_ptr<RegisteredCache> usrnand_cache;
std::unique_ptr<RegisteredCache> sysnand_cache;
std::unique_ptr<RegisteredCache> usrnand_cache;
};
} // namespace FileSys

View File

@@ -122,14 +122,16 @@ u64 XCI::GetProgramTitleID() const {
return secure_partition->GetProgramTitleID();
}
std::shared_ptr<NCA> XCI::GetProgramNCA() const {
return program;
bool XCI::HasProgramNCA() const {
return program != nullptr;
}
VirtualFile XCI::GetProgramNCAFile() const {
if (GetProgramNCA() == nullptr)
if (!HasProgramNCA()) {
return nullptr;
return GetProgramNCA()->GetBaseFile();
}
return program->GetBaseFile();
}
const std::vector<std::shared_ptr<NCA>>& XCI::GetNCAs() const {
@@ -166,10 +168,6 @@ VirtualDir XCI::GetParentDirectory() const {
return file->GetContainingDirectory();
}
bool XCI::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
return false;
}
Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
if (partitions[static_cast<std::size_t>(part)] == nullptr) {
return Loader::ResultStatus::ErrorXCIMissingPartition;

View File

@@ -80,7 +80,7 @@ public:
u64 GetProgramTitleID() const;
std::shared_ptr<NCA> GetProgramNCA() const;
bool HasProgramNCA() const;
VirtualFile GetProgramNCAFile() const;
const std::vector<std::shared_ptr<NCA>>& GetNCAs() const;
std::shared_ptr<NCA> GetNCAByType(NCAContentType type) const;
@@ -94,9 +94,6 @@ public:
VirtualDir GetParentDirectory() const override;
protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
private:
Loader::ResultStatus AddNCAFromPartition(XCIPartition part);

View File

@@ -4,10 +4,9 @@
#include <algorithm>
#include <cstring>
#include <optional>
#include <utility>
#include <boost/optional.hpp>
#include "common/logging/log.h"
#include "core/crypto/aes_util.h"
#include "core/crypto/ctr_encryption_layer.h"
@@ -97,129 +96,13 @@ union NCASectionHeader {
};
static_assert(sizeof(NCASectionHeader) == 0x200, "NCASectionHeader has incorrect size.");
bool IsValidNCA(const NCAHeader& header) {
static bool IsValidNCA(const NCAHeader& header) {
// TODO(DarkLordZach): Add NCA2/NCA0 support.
return header.magic == Common::MakeMagic('N', 'C', 'A', '3');
}
u8 NCA::GetCryptoRevision() const {
u8 master_key_id = header.crypto_type;
if (header.crypto_type_2 > master_key_id)
master_key_id = header.crypto_type_2;
if (master_key_id > 0)
--master_key_id;
return master_key_id;
}
boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType type) const {
const auto master_key_id = GetCryptoRevision();
if (!keys.HasKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, header.key_index))
return boost::none;
std::vector<u8> key_area(header.key_area.begin(), header.key_area.end());
Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(
keys.GetKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, header.key_index),
Core::Crypto::Mode::ECB);
cipher.Transcode(key_area.data(), key_area.size(), key_area.data(), Core::Crypto::Op::Decrypt);
Core::Crypto::Key128 out;
if (type == NCASectionCryptoType::XTS)
std::copy(key_area.begin(), key_area.begin() + 0x10, out.begin());
else if (type == NCASectionCryptoType::CTR || type == NCASectionCryptoType::BKTR)
std::copy(key_area.begin() + 0x20, key_area.begin() + 0x30, out.begin());
else
LOG_CRITICAL(Crypto, "Called GetKeyAreaKey on invalid NCASectionCryptoType type={:02X}",
static_cast<u8>(type));
u128 out_128{};
memcpy(out_128.data(), out.data(), 16);
LOG_DEBUG(Crypto, "called with crypto_rev={:02X}, kak_index={:02X}, key={:016X}{:016X}",
master_key_id, header.key_index, out_128[1], out_128[0]);
return out;
}
boost::optional<Core::Crypto::Key128> NCA::GetTitlekey() {
const auto master_key_id = GetCryptoRevision();
u128 rights_id{};
memcpy(rights_id.data(), header.rights_id.data(), 16);
if (rights_id == u128{}) {
status = Loader::ResultStatus::ErrorInvalidRightsID;
return boost::none;
}
auto titlekey = keys.GetKey(Core::Crypto::S128KeyType::Titlekey, rights_id[1], rights_id[0]);
if (titlekey == Core::Crypto::Key128{}) {
status = Loader::ResultStatus::ErrorMissingTitlekey;
return boost::none;
}
if (!keys.HasKey(Core::Crypto::S128KeyType::Titlekek, master_key_id)) {
status = Loader::ResultStatus::ErrorMissingTitlekek;
return boost::none;
}
Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(
keys.GetKey(Core::Crypto::S128KeyType::Titlekek, master_key_id), Core::Crypto::Mode::ECB);
cipher.Transcode(titlekey.data(), titlekey.size(), titlekey.data(), Core::Crypto::Op::Decrypt);
return titlekey;
}
VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting_offset) {
if (!encrypted)
return in;
switch (s_header.raw.header.crypto_type) {
case NCASectionCryptoType::NONE:
LOG_DEBUG(Crypto, "called with mode=NONE");
return in;
case NCASectionCryptoType::CTR:
// During normal BKTR decryption, this entire function is skipped. This is for the metadata,
// which uses the same CTR as usual.
case NCASectionCryptoType::BKTR:
LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset);
{
boost::optional<Core::Crypto::Key128> key = boost::none;
if (has_rights_id) {
status = Loader::ResultStatus::Success;
key = GetTitlekey();
if (key == boost::none) {
if (status == Loader::ResultStatus::Success)
status = Loader::ResultStatus::ErrorMissingTitlekey;
return nullptr;
}
} else {
key = GetKeyAreaKey(NCASectionCryptoType::CTR);
if (key == boost::none) {
status = Loader::ResultStatus::ErrorMissingKeyAreaKey;
return nullptr;
}
}
auto out = std::make_shared<Core::Crypto::CTREncryptionLayer>(
std::move(in), key.value(), starting_offset);
std::vector<u8> iv(16);
for (u8 i = 0; i < 8; ++i)
iv[i] = s_header.raw.section_ctr[0x8 - i - 1];
out->SetIV(iv);
return std::static_pointer_cast<VfsFile>(out);
}
case NCASectionCryptoType::XTS:
// TODO(DarkLordZach): Find a test case for XTS-encrypted NCAs
default:
LOG_ERROR(Crypto, "called with unhandled crypto type={:02X}",
static_cast<u8>(s_header.raw.header.crypto_type));
return nullptr;
}
}
NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset)
: file(std::move(file_)),
bktr_base_romfs(bktr_base_romfs_ ? std::move(bktr_base_romfs_) : nullptr) {
status = Loader::ResultStatus::Success;
: file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)) {
if (file == nullptr) {
status = Loader::ResultStatus::ErrorNullFile;
return;
@@ -231,47 +114,75 @@ NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_off
return;
}
encrypted = false;
if (!IsValidNCA(header)) {
if (header.magic == Common::MakeMagic('N', 'C', 'A', '2')) {
status = Loader::ResultStatus::ErrorNCA2;
return;
}
if (header.magic == Common::MakeMagic('N', 'C', 'A', '0')) {
status = Loader::ResultStatus::ErrorNCA0;
return;
}
NCAHeader dec_header{};
Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
cipher.XTSTranscode(&header, sizeof(NCAHeader), &dec_header, 0, 0x200,
Core::Crypto::Op::Decrypt);
if (IsValidNCA(dec_header)) {
header = dec_header;
encrypted = true;
} else {
if (dec_header.magic == Common::MakeMagic('N', 'C', 'A', '2')) {
status = Loader::ResultStatus::ErrorNCA2;
return;
}
if (dec_header.magic == Common::MakeMagic('N', 'C', 'A', '0')) {
status = Loader::ResultStatus::ErrorNCA0;
return;
}
if (!keys.HasKey(Core::Crypto::S256KeyType::Header))
status = Loader::ResultStatus::ErrorMissingHeaderKey;
else
status = Loader::ResultStatus::ErrorIncorrectHeaderKey;
return;
}
if (!HandlePotentialHeaderDecryption()) {
return;
}
has_rights_id = std::find_if_not(header.rights_id.begin(), header.rights_id.end(),
[](char c) { return c == '\0'; }) != header.rights_id.end();
has_rights_id = std::any_of(header.rights_id.begin(), header.rights_id.end(),
[](char c) { return c != '\0'; });
const std::vector<NCASectionHeader> sections = ReadSectionHeaders();
is_update = std::any_of(sections.begin(), sections.end(), [](const NCASectionHeader& header) {
return header.raw.header.crypto_type == NCASectionCryptoType::BKTR;
});
if (!ReadSections(sections, bktr_base_ivfc_offset)) {
return;
}
status = Loader::ResultStatus::Success;
}
NCA::~NCA() = default;
bool NCA::CheckSupportedNCA(const NCAHeader& nca_header) {
if (nca_header.magic == Common::MakeMagic('N', 'C', 'A', '2')) {
status = Loader::ResultStatus::ErrorNCA2;
return false;
}
if (nca_header.magic == Common::MakeMagic('N', 'C', 'A', '0')) {
status = Loader::ResultStatus::ErrorNCA0;
return false;
}
return true;
}
bool NCA::HandlePotentialHeaderDecryption() {
if (IsValidNCA(header)) {
return true;
}
if (!CheckSupportedNCA(header)) {
return false;
}
NCAHeader dec_header{};
Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
cipher.XTSTranscode(&header, sizeof(NCAHeader), &dec_header, 0, 0x200,
Core::Crypto::Op::Decrypt);
if (IsValidNCA(dec_header)) {
header = dec_header;
encrypted = true;
} else {
if (!CheckSupportedNCA(dec_header)) {
return false;
}
if (keys.HasKey(Core::Crypto::S256KeyType::Header)) {
status = Loader::ResultStatus::ErrorIncorrectHeaderKey;
} else {
status = Loader::ResultStatus::ErrorMissingHeaderKey;
}
return false;
}
return true;
}
std::vector<NCASectionHeader> NCA::ReadSectionHeaders() const {
const std::ptrdiff_t number_sections =
std::count_if(std::begin(header.section_tables), std::end(header.section_tables),
[](NCASectionTableEntry entry) { return entry.media_offset > 0; });
@@ -289,181 +200,296 @@ NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_off
file->ReadBytes(sections.data(), length_sections, SECTION_HEADER_OFFSET);
}
is_update = std::find_if(sections.begin(), sections.end(), [](const NCASectionHeader& header) {
return header.raw.header.crypto_type == NCASectionCryptoType::BKTR;
}) != sections.end();
ivfc_offset = 0;
return sections;
}
for (std::ptrdiff_t i = 0; i < number_sections; ++i) {
auto section = sections[i];
bool NCA::ReadSections(const std::vector<NCASectionHeader>& sections, u64 bktr_base_ivfc_offset) {
for (std::size_t i = 0; i < sections.size(); ++i) {
const auto& section = sections[i];
if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) {
const std::size_t base_offset =
header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER;
ivfc_offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
const std::size_t romfs_offset = base_offset + ivfc_offset;
const std::size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size;
auto raw = std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset);
auto dec = Decrypt(section, raw, romfs_offset);
if (dec == nullptr) {
if (status != Loader::ResultStatus::Success)
return;
if (has_rights_id)
status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
else
status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
return;
}
if (section.raw.header.crypto_type == NCASectionCryptoType::BKTR) {
if (section.bktr.relocation.magic != Common::MakeMagic('B', 'K', 'T', 'R') ||
section.bktr.subsection.magic != Common::MakeMagic('B', 'K', 'T', 'R')) {
status = Loader::ResultStatus::ErrorBadBKTRHeader;
return;
}
if (section.bktr.relocation.offset + section.bktr.relocation.size !=
section.bktr.subsection.offset) {
status = Loader::ResultStatus::ErrorBKTRSubsectionNotAfterRelocation;
return;
}
const u64 size =
MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset -
header.section_tables[i].media_offset);
if (section.bktr.subsection.offset + section.bktr.subsection.size != size) {
status = Loader::ResultStatus::ErrorBKTRSubsectionNotAtEnd;
return;
}
const u64 offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
RelocationBlock relocation_block{};
if (dec->ReadObject(&relocation_block, section.bktr.relocation.offset - offset) !=
sizeof(RelocationBlock)) {
status = Loader::ResultStatus::ErrorBadRelocationBlock;
return;
}
SubsectionBlock subsection_block{};
if (dec->ReadObject(&subsection_block, section.bktr.subsection.offset - offset) !=
sizeof(RelocationBlock)) {
status = Loader::ResultStatus::ErrorBadSubsectionBlock;
return;
}
std::vector<RelocationBucketRaw> relocation_buckets_raw(
(section.bktr.relocation.size - sizeof(RelocationBlock)) /
sizeof(RelocationBucketRaw));
if (dec->ReadBytes(relocation_buckets_raw.data(),
section.bktr.relocation.size - sizeof(RelocationBlock),
section.bktr.relocation.offset + sizeof(RelocationBlock) -
offset) !=
section.bktr.relocation.size - sizeof(RelocationBlock)) {
status = Loader::ResultStatus::ErrorBadRelocationBuckets;
return;
}
std::vector<SubsectionBucketRaw> subsection_buckets_raw(
(section.bktr.subsection.size - sizeof(SubsectionBlock)) /
sizeof(SubsectionBucketRaw));
if (dec->ReadBytes(subsection_buckets_raw.data(),
section.bktr.subsection.size - sizeof(SubsectionBlock),
section.bktr.subsection.offset + sizeof(SubsectionBlock) -
offset) !=
section.bktr.subsection.size - sizeof(SubsectionBlock)) {
status = Loader::ResultStatus::ErrorBadSubsectionBuckets;
return;
}
std::vector<RelocationBucket> relocation_buckets(relocation_buckets_raw.size());
std::transform(relocation_buckets_raw.begin(), relocation_buckets_raw.end(),
relocation_buckets.begin(), &ConvertRelocationBucketRaw);
std::vector<SubsectionBucket> subsection_buckets(subsection_buckets_raw.size());
std::transform(subsection_buckets_raw.begin(), subsection_buckets_raw.end(),
subsection_buckets.begin(), &ConvertSubsectionBucketRaw);
u32 ctr_low;
std::memcpy(&ctr_low, section.raw.section_ctr.data(), sizeof(ctr_low));
subsection_buckets.back().entries.push_back(
{section.bktr.relocation.offset, {0}, ctr_low});
subsection_buckets.back().entries.push_back({size, {0}, 0});
boost::optional<Core::Crypto::Key128> key = boost::none;
if (encrypted) {
if (has_rights_id) {
status = Loader::ResultStatus::Success;
key = GetTitlekey();
if (key == boost::none) {
status = Loader::ResultStatus::ErrorMissingTitlekey;
return;
}
} else {
key = GetKeyAreaKey(NCASectionCryptoType::BKTR);
if (key == boost::none) {
status = Loader::ResultStatus::ErrorMissingKeyAreaKey;
return;
}
}
}
if (bktr_base_romfs == nullptr) {
status = Loader::ResultStatus::ErrorMissingBKTRBaseRomFS;
return;
}
auto bktr = std::make_shared<BKTR>(
bktr_base_romfs, std::make_shared<OffsetVfsFile>(file, romfs_size, base_offset),
relocation_block, relocation_buckets, subsection_block, subsection_buckets,
encrypted, encrypted ? key.get() : Core::Crypto::Key128{}, base_offset,
bktr_base_ivfc_offset, section.raw.section_ctr);
// BKTR applies to entire IVFC, so make an offset version to level 6
files.push_back(std::make_shared<OffsetVfsFile>(
bktr, romfs_size, section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset));
romfs = files.back();
} else {
files.push_back(std::move(dec));
romfs = files.back();
if (!ReadRomFSSection(section, header.section_tables[i], bktr_base_ivfc_offset)) {
return false;
}
} else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) {
u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) *
MEDIA_OFFSET_MULTIPLIER) +
section.pfs0.pfs0_header_offset;
u64 size = MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset -
header.section_tables[i].media_offset);
auto dec =
Decrypt(section, std::make_shared<OffsetVfsFile>(file, size, offset), offset);
if (dec != nullptr) {
auto npfs = std::make_shared<PartitionFilesystem>(std::move(dec));
if (npfs->GetStatus() == Loader::ResultStatus::Success) {
dirs.push_back(std::move(npfs));
if (IsDirectoryExeFS(dirs.back()))
exefs = dirs.back();
} else {
if (has_rights_id)
status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
else
status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
return;
}
} else {
if (status != Loader::ResultStatus::Success)
return;
if (has_rights_id)
status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
else
status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
return;
if (!ReadPFS0Section(section, header.section_tables[i])) {
return false;
}
}
}
status = Loader::ResultStatus::Success;
return true;
}
NCA::~NCA() = default;
bool NCA::ReadRomFSSection(const NCASectionHeader& section, const NCASectionTableEntry& entry,
u64 bktr_base_ivfc_offset) {
const std::size_t base_offset = entry.media_offset * MEDIA_OFFSET_MULTIPLIER;
ivfc_offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
const std::size_t romfs_offset = base_offset + ivfc_offset;
const std::size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size;
auto raw = std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset);
auto dec = Decrypt(section, raw, romfs_offset);
if (dec == nullptr) {
if (status != Loader::ResultStatus::Success)
return false;
if (has_rights_id)
status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
else
status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
return false;
}
if (section.raw.header.crypto_type == NCASectionCryptoType::BKTR) {
if (section.bktr.relocation.magic != Common::MakeMagic('B', 'K', 'T', 'R') ||
section.bktr.subsection.magic != Common::MakeMagic('B', 'K', 'T', 'R')) {
status = Loader::ResultStatus::ErrorBadBKTRHeader;
return false;
}
if (section.bktr.relocation.offset + section.bktr.relocation.size !=
section.bktr.subsection.offset) {
status = Loader::ResultStatus::ErrorBKTRSubsectionNotAfterRelocation;
return false;
}
const u64 size = MEDIA_OFFSET_MULTIPLIER * (entry.media_end_offset - entry.media_offset);
if (section.bktr.subsection.offset + section.bktr.subsection.size != size) {
status = Loader::ResultStatus::ErrorBKTRSubsectionNotAtEnd;
return false;
}
const u64 offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
RelocationBlock relocation_block{};
if (dec->ReadObject(&relocation_block, section.bktr.relocation.offset - offset) !=
sizeof(RelocationBlock)) {
status = Loader::ResultStatus::ErrorBadRelocationBlock;
return false;
}
SubsectionBlock subsection_block{};
if (dec->ReadObject(&subsection_block, section.bktr.subsection.offset - offset) !=
sizeof(RelocationBlock)) {
status = Loader::ResultStatus::ErrorBadSubsectionBlock;
return false;
}
std::vector<RelocationBucketRaw> relocation_buckets_raw(
(section.bktr.relocation.size - sizeof(RelocationBlock)) / sizeof(RelocationBucketRaw));
if (dec->ReadBytes(relocation_buckets_raw.data(),
section.bktr.relocation.size - sizeof(RelocationBlock),
section.bktr.relocation.offset + sizeof(RelocationBlock) - offset) !=
section.bktr.relocation.size - sizeof(RelocationBlock)) {
status = Loader::ResultStatus::ErrorBadRelocationBuckets;
return false;
}
std::vector<SubsectionBucketRaw> subsection_buckets_raw(
(section.bktr.subsection.size - sizeof(SubsectionBlock)) / sizeof(SubsectionBucketRaw));
if (dec->ReadBytes(subsection_buckets_raw.data(),
section.bktr.subsection.size - sizeof(SubsectionBlock),
section.bktr.subsection.offset + sizeof(SubsectionBlock) - offset) !=
section.bktr.subsection.size - sizeof(SubsectionBlock)) {
status = Loader::ResultStatus::ErrorBadSubsectionBuckets;
return false;
}
std::vector<RelocationBucket> relocation_buckets(relocation_buckets_raw.size());
std::transform(relocation_buckets_raw.begin(), relocation_buckets_raw.end(),
relocation_buckets.begin(), &ConvertRelocationBucketRaw);
std::vector<SubsectionBucket> subsection_buckets(subsection_buckets_raw.size());
std::transform(subsection_buckets_raw.begin(), subsection_buckets_raw.end(),
subsection_buckets.begin(), &ConvertSubsectionBucketRaw);
u32 ctr_low;
std::memcpy(&ctr_low, section.raw.section_ctr.data(), sizeof(ctr_low));
subsection_buckets.back().entries.push_back({section.bktr.relocation.offset, {0}, ctr_low});
subsection_buckets.back().entries.push_back({size, {0}, 0});
std::optional<Core::Crypto::Key128> key = {};
if (encrypted) {
if (has_rights_id) {
status = Loader::ResultStatus::Success;
key = GetTitlekey();
if (!key) {
status = Loader::ResultStatus::ErrorMissingTitlekey;
return false;
}
} else {
key = GetKeyAreaKey(NCASectionCryptoType::BKTR);
if (!key) {
status = Loader::ResultStatus::ErrorMissingKeyAreaKey;
return false;
}
}
}
if (bktr_base_romfs == nullptr) {
status = Loader::ResultStatus::ErrorMissingBKTRBaseRomFS;
return false;
}
auto bktr = std::make_shared<BKTR>(
bktr_base_romfs, std::make_shared<OffsetVfsFile>(file, romfs_size, base_offset),
relocation_block, relocation_buckets, subsection_block, subsection_buckets, encrypted,
encrypted ? *key : Core::Crypto::Key128{}, base_offset, bktr_base_ivfc_offset,
section.raw.section_ctr);
// BKTR applies to entire IVFC, so make an offset version to level 6
files.push_back(std::make_shared<OffsetVfsFile>(
bktr, romfs_size, section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset));
} else {
files.push_back(std::move(dec));
}
romfs = files.back();
return true;
}
bool NCA::ReadPFS0Section(const NCASectionHeader& section, const NCASectionTableEntry& entry) {
const u64 offset = (static_cast<u64>(entry.media_offset) * MEDIA_OFFSET_MULTIPLIER) +
section.pfs0.pfs0_header_offset;
const u64 size = MEDIA_OFFSET_MULTIPLIER * (entry.media_end_offset - entry.media_offset);
auto dec = Decrypt(section, std::make_shared<OffsetVfsFile>(file, size, offset), offset);
if (dec != nullptr) {
auto npfs = std::make_shared<PartitionFilesystem>(std::move(dec));
if (npfs->GetStatus() == Loader::ResultStatus::Success) {
dirs.push_back(std::move(npfs));
if (IsDirectoryExeFS(dirs.back()))
exefs = dirs.back();
} else {
if (has_rights_id)
status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
else
status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
return false;
}
} else {
if (status != Loader::ResultStatus::Success)
return false;
if (has_rights_id)
status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
else
status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
return false;
}
return true;
}
u8 NCA::GetCryptoRevision() const {
u8 master_key_id = header.crypto_type;
if (header.crypto_type_2 > master_key_id)
master_key_id = header.crypto_type_2;
if (master_key_id > 0)
--master_key_id;
return master_key_id;
}
std::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType type) const {
const auto master_key_id = GetCryptoRevision();
if (!keys.HasKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, header.key_index))
return {};
std::vector<u8> key_area(header.key_area.begin(), header.key_area.end());
Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(
keys.GetKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, header.key_index),
Core::Crypto::Mode::ECB);
cipher.Transcode(key_area.data(), key_area.size(), key_area.data(), Core::Crypto::Op::Decrypt);
Core::Crypto::Key128 out;
if (type == NCASectionCryptoType::XTS)
std::copy(key_area.begin(), key_area.begin() + 0x10, out.begin());
else if (type == NCASectionCryptoType::CTR || type == NCASectionCryptoType::BKTR)
std::copy(key_area.begin() + 0x20, key_area.begin() + 0x30, out.begin());
else
LOG_CRITICAL(Crypto, "Called GetKeyAreaKey on invalid NCASectionCryptoType type={:02X}",
static_cast<u8>(type));
u128 out_128{};
memcpy(out_128.data(), out.data(), 16);
LOG_TRACE(Crypto, "called with crypto_rev={:02X}, kak_index={:02X}, key={:016X}{:016X}",
master_key_id, header.key_index, out_128[1], out_128[0]);
return out;
}
std::optional<Core::Crypto::Key128> NCA::GetTitlekey() {
const auto master_key_id = GetCryptoRevision();
u128 rights_id{};
memcpy(rights_id.data(), header.rights_id.data(), 16);
if (rights_id == u128{}) {
status = Loader::ResultStatus::ErrorInvalidRightsID;
return {};
}
auto titlekey = keys.GetKey(Core::Crypto::S128KeyType::Titlekey, rights_id[1], rights_id[0]);
if (titlekey == Core::Crypto::Key128{}) {
status = Loader::ResultStatus::ErrorMissingTitlekey;
return {};
}
if (!keys.HasKey(Core::Crypto::S128KeyType::Titlekek, master_key_id)) {
status = Loader::ResultStatus::ErrorMissingTitlekek;
return {};
}
Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(
keys.GetKey(Core::Crypto::S128KeyType::Titlekek, master_key_id), Core::Crypto::Mode::ECB);
cipher.Transcode(titlekey.data(), titlekey.size(), titlekey.data(), Core::Crypto::Op::Decrypt);
return titlekey;
}
VirtualFile NCA::Decrypt(const NCASectionHeader& s_header, VirtualFile in, u64 starting_offset) {
if (!encrypted)
return in;
switch (s_header.raw.header.crypto_type) {
case NCASectionCryptoType::NONE:
LOG_DEBUG(Crypto, "called with mode=NONE");
return in;
case NCASectionCryptoType::CTR:
// During normal BKTR decryption, this entire function is skipped. This is for the metadata,
// which uses the same CTR as usual.
case NCASectionCryptoType::BKTR:
LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset);
{
std::optional<Core::Crypto::Key128> key = {};
if (has_rights_id) {
status = Loader::ResultStatus::Success;
key = GetTitlekey();
if (!key) {
if (status == Loader::ResultStatus::Success)
status = Loader::ResultStatus::ErrorMissingTitlekey;
return nullptr;
}
} else {
key = GetKeyAreaKey(NCASectionCryptoType::CTR);
if (!key) {
status = Loader::ResultStatus::ErrorMissingKeyAreaKey;
return nullptr;
}
}
auto out = std::make_shared<Core::Crypto::CTREncryptionLayer>(std::move(in), *key,
starting_offset);
std::vector<u8> iv(16);
for (u8 i = 0; i < 8; ++i)
iv[i] = s_header.raw.section_ctr[0x8 - i - 1];
out->SetIV(iv);
return std::static_pointer_cast<VfsFile>(out);
}
case NCASectionCryptoType::XTS:
// TODO(DarkLordZach): Find a test case for XTS-encrypted NCAs
default:
LOG_ERROR(Crypto, "called with unhandled crypto type={:02X}",
static_cast<u8>(s_header.raw.header.crypto_type));
return nullptr;
}
}
Loader::ResultStatus NCA::GetStatus() const {
return status;
@@ -519,7 +545,4 @@ u64 NCA::GetBaseIVFCOffset() const {
return ivfc_offset;
}
bool NCA::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
return false;
}
} // namespace FileSys

View File

@@ -6,9 +6,10 @@
#include <array>
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include <boost/optional.hpp>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
@@ -73,8 +74,6 @@ inline bool IsDirectoryExeFS(const std::shared_ptr<VfsDirectory>& pfs) {
return pfs->GetFile("main") != nullptr && pfs->GetFile("main.npdm") != nullptr;
}
bool IsValidNCA(const NCAHeader& header);
// An implementation of VfsDirectory that represents a Nintendo Content Archive (NCA) conatiner.
// After construction, use GetStatus to determine if the file is valid and ready to be used.
class NCA : public ReadOnlyVfsDirectory {
@@ -102,14 +101,20 @@ public:
// Returns the base ivfc offset used in BKTR patching.
u64 GetBaseIVFCOffset() const;
protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
private:
bool CheckSupportedNCA(const NCAHeader& header);
bool HandlePotentialHeaderDecryption();
std::vector<NCASectionHeader> ReadSectionHeaders() const;
bool ReadSections(const std::vector<NCASectionHeader>& sections, u64 bktr_base_ivfc_offset);
bool ReadRomFSSection(const NCASectionHeader& section, const NCASectionTableEntry& entry,
u64 bktr_base_ivfc_offset);
bool ReadPFS0Section(const NCASectionHeader& section, const NCASectionTableEntry& entry);
u8 GetCryptoRevision() const;
boost::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const;
boost::optional<Core::Crypto::Key128> GetTitlekey();
VirtualFile Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset);
std::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const;
std::optional<Core::Crypto::Key128> GetTitlekey();
VirtualFile Decrypt(const NCASectionHeader& header, VirtualFile in, u64 starting_offset);
std::vector<VirtualDir> dirs;
std::vector<VirtualFile> files;
@@ -118,15 +123,15 @@ private:
VirtualDir exefs = nullptr;
VirtualFile file;
VirtualFile bktr_base_romfs;
u64 ivfc_offset;
u64 ivfc_offset = 0;
NCAHeader header{};
bool has_rights_id{};
Loader::ResultStatus status{};
bool encrypted;
bool is_update;
bool encrypted = false;
bool is_update = false;
Core::Crypto::KeyManager keys;
};

View File

@@ -17,11 +17,13 @@ const std::array<const char*, 15> LANGUAGE_NAMES = {
};
std::string LanguageEntry::GetApplicationName() const {
return Common::StringFromFixedZeroTerminatedBuffer(application_name.data(), 0x200);
return Common::StringFromFixedZeroTerminatedBuffer(application_name.data(),
application_name.size());
}
std::string LanguageEntry::GetDeveloperName() const {
return Common::StringFromFixedZeroTerminatedBuffer(developer_name.data(), 0x100);
return Common::StringFromFixedZeroTerminatedBuffer(developer_name.data(),
developer_name.size());
}
NACP::NACP(VirtualFile file) : raw(std::make_unique<RawNACP>()) {
@@ -56,7 +58,12 @@ u64 NACP::GetTitleId() const {
return raw->title_id;
}
u64 NACP::GetDLCBaseTitleId() const {
return raw->dlc_base_title_id;
}
std::string NACP::GetVersionString() const {
return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(), 0x10);
return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(),
raw->version_string.size());
}
} // namespace FileSys

View File

@@ -79,6 +79,7 @@ public:
std::string GetApplicationName(Language language = Language::Default) const;
std::string GetDeveloperName(Language language = Language::Default) const;
u64 GetTitleId() const;
u64 GetDLCBaseTitleId() const;
std::string GetVersionString() const;
private:

View File

@@ -27,7 +27,6 @@
#include <map>
#include <memory>
#include <string>
#include <boost/detail/container_fwd.hpp>
#include "common/common_types.h"
#include "core/file_sys/vfs.h"

View File

@@ -99,16 +99,16 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
u16 rle_size{};
if (ips->ReadObject(&rle_size, offset) != sizeof(u16))
return nullptr;
rle_size = Common::swap16(data_size);
rle_size = Common::swap16(rle_size);
offset += sizeof(u16);
const auto data = ips->ReadByte(offset++);
if (data == boost::none)
if (!data)
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);
std::memset(in_data.data() + real_offset, *data, rle_size);
} else { // Standard Patch
auto read = data_size;
if (real_offset + read > in_data.size())

View File

@@ -83,7 +83,7 @@ std::vector<std::shared_ptr<VfsFile>> PartitionFilesystem::GetFiles() const {
}
std::vector<std::shared_ptr<VfsDirectory>> PartitionFilesystem::GetSubdirectories() const {
return pfs_dirs;
return {};
}
std::string PartitionFilesystem::GetName() const {
@@ -103,18 +103,4 @@ void PartitionFilesystem::PrintDebugInfo() const {
pfs_files[i]->GetName(), pfs_files[i]->GetSize());
}
}
bool PartitionFilesystem::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
const auto iter = std::find(pfs_files.begin(), pfs_files.end(), file);
if (iter == pfs_files.end())
return false;
const std::ptrdiff_t offset = std::distance(pfs_files.begin(), iter);
pfs_files[offset] = std::move(pfs_files.back());
pfs_files.pop_back();
pfs_dirs.emplace_back(std::move(dir));
return true;
}
} // namespace FileSys

View File

@@ -35,9 +35,6 @@ public:
std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
void PrintDebugInfo() const;
protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
private:
struct Header {
u32_le magic;
@@ -84,7 +81,6 @@ private:
std::size_t content_offset = 0;
std::vector<VirtualFile> pfs_files;
std::vector<VirtualDir> pfs_dirs;
};
} // namespace FileSys

View File

@@ -61,13 +61,12 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
// Game Updates
const auto update_tid = GetUpdateTitleID(title_id);
const auto update = installed->GetEntry(update_tid, ContentRecordType::Program);
if (update != nullptr) {
if (update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS &&
update->GetExeFS() != nullptr) {
LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully",
FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0)));
exefs = update->GetExeFS();
}
if (update != nullptr && update->GetExeFS() != nullptr &&
update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) {
LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully",
FormatTitleVersion(installed->GetEntryVersion(update_tid).value_or(0)));
exefs = update->GetExeFS();
}
return exefs;
@@ -168,7 +167,8 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
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) {
if ((type != ContentRecordType::Program && type != ContentRecordType::Data) ||
load_dir == nullptr || load_dir->GetSize() <= 0) {
return;
}
@@ -214,8 +214,14 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
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));
const auto log_string = fmt::format("Patching RomFS for title_id={:016X}, type={:02X}",
title_id, static_cast<u8>(type))
.c_str();
if (type == ContentRecordType::Program || type == ContentRecordType::Data)
LOG_INFO(Loader, log_string);
else
LOG_DEBUG(Loader, log_string);
if (romfs == nullptr)
return romfs;
@@ -230,7 +236,7 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content
if (new_nca->GetStatus() == Loader::ResultStatus::Success &&
new_nca->GetRomFS() != nullptr) {
LOG_INFO(Loader, " RomFS: Update ({}) applied successfully",
FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0)));
FormatTitleVersion(installed->GetEntryVersion(update_tid).value_or(0)));
romfs = new_nca->GetRomFS();
}
} else if (update_raw != nullptr) {
@@ -274,12 +280,11 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
} 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) {
if (meta_ver.value_or(0) == 0) {
out.insert_or_assign("Update", "");
} else {
out.insert_or_assign(
"Update",
FormatTitleVersion(meta_ver.get(), TitleVersionFormat::ThreeElements));
"Update", FormatTitleVersion(*meta_ver, TitleVersionFormat::ThreeElements));
}
} else if (update_raw != nullptr) {
out.insert_or_assign("Update", "PACKED");
@@ -346,7 +351,7 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
}
std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const {
const auto& installed{Service::FileSystem::GetUnionContents()};
const auto installed{Service::FileSystem::GetUnionContents()};
const auto base_control_nca = installed->GetEntry(title_id, ContentRecordType::Control);
if (base_control_nca == nullptr)

View File

@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <regex>
#include <mbedtls/sha256.h>
#include "common/assert.h"
@@ -30,6 +31,14 @@ bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs)
return (lhs.title_id < rhs.title_id) || (lhs.title_id == rhs.title_id && lhs.type < rhs.type);
}
bool operator==(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) {
return std::tie(lhs.title_id, lhs.type) == std::tie(rhs.title_id, rhs.type);
}
bool operator!=(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) {
return !operator==(lhs, rhs);
}
static bool FollowsTwoDigitDirFormat(std::string_view name) {
static const std::regex two_digit_regex("000000[0-9A-F]{2}", std::regex_constants::ECMAScript |
std::regex_constants::icase);
@@ -150,28 +159,28 @@ VirtualFile RegisteredCache::GetFileAtID(NcaID id) const {
return file;
}
static boost::optional<NcaID> CheckMapForContentRecord(
static std::optional<NcaID> CheckMapForContentRecord(
const boost::container::flat_map<u64, CNMT>& map, u64 title_id, ContentRecordType type) {
if (map.find(title_id) == map.end())
return boost::none;
return {};
const auto& cnmt = map.at(title_id);
const auto iter = std::find_if(cnmt.GetContentRecords().begin(), cnmt.GetContentRecords().end(),
[type](const ContentRecord& rec) { return rec.type == type; });
if (iter == cnmt.GetContentRecords().end())
return boost::none;
return {};
return boost::make_optional(iter->nca_id);
return std::make_optional(iter->nca_id);
}
boost::optional<NcaID> RegisteredCache::GetNcaIDFromMetadata(u64 title_id,
ContentRecordType type) const {
std::optional<NcaID> RegisteredCache::GetNcaIDFromMetadata(u64 title_id,
ContentRecordType type) const {
if (type == ContentRecordType::Meta && meta_id.find(title_id) != meta_id.end())
return meta_id.at(title_id);
const auto res1 = CheckMapForContentRecord(yuzu_meta, title_id, type);
if (res1 != boost::none)
if (res1)
return res1;
return CheckMapForContentRecord(meta, title_id, type);
}
@@ -274,17 +283,14 @@ bool RegisteredCache::HasEntry(RegisteredCacheEntry entry) const {
VirtualFile RegisteredCache::GetEntryUnparsed(u64 title_id, ContentRecordType type) const {
const auto id = GetNcaIDFromMetadata(title_id, type);
if (id == boost::none)
return nullptr;
return GetFileAtID(id.get());
return id ? GetFileAtID(*id) : nullptr;
}
VirtualFile RegisteredCache::GetEntryUnparsed(RegisteredCacheEntry entry) const {
return GetEntryUnparsed(entry.title_id, entry.type);
}
boost::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const {
std::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const {
const auto meta_iter = meta.find(title_id);
if (meta_iter != meta.end())
return meta_iter->second.GetTitleVersion();
@@ -293,29 +299,26 @@ boost::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const {
if (yuzu_meta_iter != yuzu_meta.end())
return yuzu_meta_iter->second.GetTitleVersion();
return boost::none;
return {};
}
VirtualFile RegisteredCache::GetEntryRaw(u64 title_id, ContentRecordType type) const {
const auto id = GetNcaIDFromMetadata(title_id, type);
if (id == boost::none)
return nullptr;
return parser(GetFileAtID(id.get()), id.get());
return id ? parser(GetFileAtID(*id), *id) : nullptr;
}
VirtualFile RegisteredCache::GetEntryRaw(RegisteredCacheEntry entry) const {
return GetEntryRaw(entry.title_id, entry.type);
}
std::shared_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType type) const {
std::unique_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType type) const {
const auto raw = GetEntryRaw(title_id, type);
if (raw == nullptr)
return nullptr;
return std::make_shared<NCA>(raw);
return std::make_unique<NCA>(raw);
}
std::shared_ptr<NCA> RegisteredCache::GetEntry(RegisteredCacheEntry entry) const {
std::unique_ptr<NCA> RegisteredCache::GetEntry(RegisteredCacheEntry entry) const {
return GetEntry(entry.title_id, entry.type);
}
@@ -355,8 +358,8 @@ std::vector<RegisteredCacheEntry> RegisteredCache::ListEntries() const {
}
std::vector<RegisteredCacheEntry> RegisteredCache::ListEntriesFilter(
boost::optional<TitleType> title_type, boost::optional<ContentRecordType> record_type,
boost::optional<u64> title_id) const {
std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
std::optional<u64> title_id) const {
std::vector<RegisteredCacheEntry> out;
IterateAllMetadata<RegisteredCacheEntry>(
out,
@@ -364,11 +367,11 @@ std::vector<RegisteredCacheEntry> RegisteredCache::ListEntriesFilter(
return RegisteredCacheEntry{c.GetTitleID(), r.type};
},
[&title_type, &record_type, &title_id](const CNMT& c, const ContentRecord& r) {
if (title_type != boost::none && title_type.get() != c.GetType())
if (title_type && *title_type != c.GetType())
return false;
if (record_type != boost::none && record_type.get() != r.type)
if (record_type && *record_type != r.type)
return false;
if (title_id != boost::none && title_id.get() != c.GetTitleID())
if (title_id && *title_id != c.GetTitleID())
return false;
return true;
});
@@ -450,7 +453,7 @@ InstallResult RegisteredCache::InstallEntry(std::shared_ptr<NCA> nca, TitleType
InstallResult RegisteredCache::RawInstallNCA(std::shared_ptr<NCA> nca, const VfsCopyFunction& copy,
bool overwrite_if_exists,
boost::optional<NcaID> override_id) {
std::optional<NcaID> override_id) {
const auto in = nca->GetBaseFile();
Core::Crypto::SHA256Hash hash{};
@@ -459,12 +462,12 @@ InstallResult RegisteredCache::RawInstallNCA(std::shared_ptr<NCA> nca, const Vfs
// game is massive), we're going to cheat and only hash the first MB of the NCA.
// Also, for XCIs the NcaID matters, so if the override id isn't none, use that.
NcaID id{};
if (override_id == boost::none) {
if (override_id) {
id = *override_id;
} else {
const auto& data = in->ReadBytes(0x100000);
mbedtls_sha256(data.data(), data.size(), hash.data(), 0);
memcpy(id.data(), hash.data(), 16);
} else {
id = override_id.get();
}
std::string path = GetRelativePathFromNcaID(id, false, true);
@@ -516,7 +519,7 @@ bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) {
}) != yuzu_meta.end();
}
RegisteredCacheUnion::RegisteredCacheUnion(std::vector<std::shared_ptr<RegisteredCache>> caches)
RegisteredCacheUnion::RegisteredCacheUnion(std::vector<RegisteredCache*> caches)
: caches(std::move(caches)) {}
void RegisteredCacheUnion::Refresh() {
@@ -534,14 +537,14 @@ bool RegisteredCacheUnion::HasEntry(RegisteredCacheEntry entry) const {
return HasEntry(entry.title_id, entry.type);
}
boost::optional<u32> RegisteredCacheUnion::GetEntryVersion(u64 title_id) const {
std::optional<u32> RegisteredCacheUnion::GetEntryVersion(u64 title_id) const {
for (const auto& c : caches) {
const auto res = c->GetEntryVersion(title_id);
if (res != boost::none)
if (res)
return res;
}
return boost::none;
return {};
}
VirtualFile RegisteredCacheUnion::GetEntryUnparsed(u64 title_id, ContentRecordType type) const {
@@ -572,14 +575,14 @@ VirtualFile RegisteredCacheUnion::GetEntryRaw(RegisteredCacheEntry entry) const
return GetEntryRaw(entry.title_id, entry.type);
}
std::shared_ptr<NCA> RegisteredCacheUnion::GetEntry(u64 title_id, ContentRecordType type) const {
std::unique_ptr<NCA> RegisteredCacheUnion::GetEntry(u64 title_id, ContentRecordType type) const {
const auto raw = GetEntryRaw(title_id, type);
if (raw == nullptr)
return nullptr;
return std::make_shared<NCA>(raw);
return std::make_unique<NCA>(raw);
}
std::shared_ptr<NCA> RegisteredCacheUnion::GetEntry(RegisteredCacheEntry entry) const {
std::unique_ptr<NCA> RegisteredCacheUnion::GetEntry(RegisteredCacheEntry entry) const {
return GetEntry(entry.title_id, entry.type);
}
@@ -593,12 +596,15 @@ std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntries() const {
},
[](const CNMT& c, const ContentRecord& r) { return true; });
}
std::sort(out.begin(), out.end());
out.erase(std::unique(out.begin(), out.end()), out.end());
return out;
}
std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntriesFilter(
boost::optional<TitleType> title_type, boost::optional<ContentRecordType> record_type,
boost::optional<u64> title_id) const {
std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
std::optional<u64> title_id) const {
std::vector<RegisteredCacheEntry> out;
for (const auto& c : caches) {
c->IterateAllMetadata<RegisteredCacheEntry>(
@@ -607,15 +613,18 @@ std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntriesFilter(
return RegisteredCacheEntry{c.GetTitleID(), r.type};
},
[&title_type, &record_type, &title_id](const CNMT& c, const ContentRecord& r) {
if (title_type != boost::none && title_type.get() != c.GetType())
if (title_type && *title_type != c.GetType())
return false;
if (record_type != boost::none && record_type.get() != r.type)
if (record_type && *record_type != r.type)
return false;
if (title_id != boost::none && title_id.get() != c.GetTitleID())
if (title_id && *title_id != c.GetTitleID())
return false;
return true;
});
}
std::sort(out.begin(), out.end());
out.erase(std::unique(out.begin(), out.end()), out.end());
return out;
}
} // namespace FileSys

View File

@@ -50,6 +50,10 @@ constexpr u64 GetUpdateTitleID(u64 base_title_id) {
// boost flat_map requires operator< for O(log(n)) lookups.
bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs);
// std unique requires operator== to identify duplicates.
bool operator==(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs);
bool operator!=(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs);
/*
* A class that catalogues NCAs in the registered directory structure.
* Nintendo's registered format follows this structure:
@@ -60,8 +64,8 @@ bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs)
* | 00
* | 01 <- Actual content split along 4GB boundaries. (optional)
*
* (This impl also supports substituting the nca dir for an nca file, as that's more convenient when
* 4GB splitting can be ignored.)
* (This impl also supports substituting the nca dir for an nca file, as that's more convenient
* when 4GB splitting can be ignored.)
*/
class RegisteredCache {
friend class RegisteredCacheUnion;
@@ -80,7 +84,7 @@ public:
bool HasEntry(u64 title_id, ContentRecordType type) const;
bool HasEntry(RegisteredCacheEntry entry) const;
boost::optional<u32> GetEntryVersion(u64 title_id) const;
std::optional<u32> GetEntryVersion(u64 title_id) const;
VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const;
VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const;
@@ -88,15 +92,14 @@ public:
VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const;
VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const;
std::shared_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const;
std::shared_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const;
std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
std::vector<RegisteredCacheEntry> ListEntries() const;
// If a parameter is not boost::none, it will be filtered for from all entries.
// If a parameter is not std::nullopt, it will be filtered for from all entries.
std::vector<RegisteredCacheEntry> ListEntriesFilter(
boost::optional<TitleType> title_type = boost::none,
boost::optional<ContentRecordType> record_type = boost::none,
boost::optional<u64> title_id = boost::none) const;
std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
std::optional<u64> title_id = {}) const;
// Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure
// there is a meta NCA and all of them are accessible.
@@ -121,12 +124,11 @@ private:
std::vector<NcaID> AccumulateFiles() const;
void ProcessFiles(const std::vector<NcaID>& ids);
void AccumulateYuzuMeta();
boost::optional<NcaID> GetNcaIDFromMetadata(u64 title_id, ContentRecordType type) const;
std::optional<NcaID> GetNcaIDFromMetadata(u64 title_id, ContentRecordType type) const;
VirtualFile GetFileAtID(NcaID id) const;
VirtualFile OpenFileOrDirectoryConcat(const VirtualDir& dir, std::string_view path) const;
InstallResult RawInstallNCA(std::shared_ptr<NCA> nca, const VfsCopyFunction& copy,
bool overwrite_if_exists,
boost::optional<NcaID> override_id = boost::none);
bool overwrite_if_exists, std::optional<NcaID> override_id = {});
bool RawInstallYuzuMeta(const CNMT& cnmt);
VirtualDir dir;
@@ -142,14 +144,14 @@ private:
// Combines multiple RegisteredCaches (i.e. SysNAND, UserNAND, SDMC) into one interface.
class RegisteredCacheUnion {
public:
explicit RegisteredCacheUnion(std::vector<std::shared_ptr<RegisteredCache>> caches);
explicit RegisteredCacheUnion(std::vector<RegisteredCache*> caches);
void Refresh();
bool HasEntry(u64 title_id, ContentRecordType type) const;
bool HasEntry(RegisteredCacheEntry entry) const;
boost::optional<u32> GetEntryVersion(u64 title_id) const;
std::optional<u32> GetEntryVersion(u64 title_id) const;
VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const;
VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const;
@@ -157,18 +159,17 @@ public:
VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const;
VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const;
std::shared_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const;
std::shared_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const;
std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
std::vector<RegisteredCacheEntry> ListEntries() const;
// If a parameter is not boost::none, it will be filtered for from all entries.
// If a parameter is not std::nullopt, it will be filtered for from all entries.
std::vector<RegisteredCacheEntry> ListEntriesFilter(
boost::optional<TitleType> title_type = boost::none,
boost::optional<ContentRecordType> record_type = boost::none,
boost::optional<u64> title_id = boost::none) const;
std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
std::optional<u64> title_id = {}) const;
private:
std::vector<std::shared_ptr<RegisteredCache>> caches;
std::vector<RegisteredCache*> caches;
};
} // namespace FileSys

View File

@@ -51,6 +51,13 @@ ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescr
meta.title_id);
}
if (meta.type == SaveDataType::DeviceSaveData && meta.user_id != u128{0, 0}) {
LOG_WARNING(Service_FS,
"Possibly incorrect SaveDataDescriptor, type is DeviceSaveData but user_id is "
"non-zero ({:016X}{:016X})",
meta.user_id[1], meta.user_id[0]);
}
std::string save_directory =
GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id);
@@ -92,6 +99,9 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
case SaveDataSpaceId::NandUser:
out = "/user/";
break;
case SaveDataSpaceId::TemporaryStorage:
out = "/temp/";
break;
default:
ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space));
}
@@ -100,10 +110,11 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
case SaveDataType::SystemSaveData:
return fmt::format("{}save/{:016X}/{:016X}{:016X}", out, save_id, user_id[1], user_id[0]);
case SaveDataType::SaveData:
case SaveDataType::DeviceSaveData:
return fmt::format("{}save/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
title_id);
case SaveDataType::TemporaryStorage:
return fmt::format("{}temp/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
return fmt::format("{}{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
title_id);
default:
ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type));

View File

@@ -10,10 +10,10 @@
namespace FileSys {
SDMCFactory::SDMCFactory(VirtualDir dir_)
: dir(std::move(dir_)), contents(std::make_shared<RegisteredCache>(
: dir(std::move(dir_)), contents(std::make_unique<RegisteredCache>(
GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/registered"),
[](const VirtualFile& file, const NcaID& id) {
return std::make_shared<NAX>(file, id)->GetDecrypted();
return NAX{file, id}.GetDecrypted();
})) {}
SDMCFactory::~SDMCFactory() = default;
@@ -22,8 +22,8 @@ ResultVal<VirtualDir> SDMCFactory::Open() {
return MakeResult<VirtualDir>(dir);
}
std::shared_ptr<RegisteredCache> SDMCFactory::GetSDMCContents() const {
return contents;
RegisteredCache* SDMCFactory::GetSDMCContents() const {
return contents.get();
}
} // namespace FileSys

View File

@@ -19,12 +19,12 @@ public:
~SDMCFactory();
ResultVal<VirtualDir> Open();
std::shared_ptr<RegisteredCache> GetSDMCContents() const;
RegisteredCache* GetSDMCContents() const;
private:
VirtualDir dir;
std::shared_ptr<RegisteredCache> contents;
std::unique_ptr<RegisteredCache> contents;
};
} // namespace FileSys

View File

@@ -205,10 +205,6 @@ VirtualDir NSP::GetParentDirectory() const {
return file->GetContainingDirectory();
}
bool NSP::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
return false;
}
void NSP::InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files) {
exefs = pfs;

View File

@@ -55,9 +55,6 @@ public:
VirtualDir GetParentDirectory() const override;
protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
private:
void InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files);
void ReadNCAs(const std::vector<VirtualFile>& files);

View File

@@ -167,13 +167,13 @@ std::string VfsFile::GetExtension() const {
VfsDirectory::~VfsDirectory() = default;
boost::optional<u8> VfsFile::ReadByte(std::size_t offset) const {
std::optional<u8> VfsFile::ReadByte(std::size_t offset) const {
u8 out{};
std::size_t size = Read(&out, 1, offset);
if (size == 1)
return out;
return boost::none;
return {};
}
std::vector<u8> VfsFile::ReadBytes(std::size_t size, std::size_t offset) const {
@@ -472,10 +472,14 @@ bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, std::size_t blo
std::vector<u8> temp(std::min(block_size, src->GetSize()));
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);
if (dest->Write(temp.data(), read, i) != read)
if (src->Read(temp.data(), read, i) != read) {
return false;
}
if (dest->Write(temp.data(), read, i) != read) {
return false;
}
}
return true;

View File

@@ -4,28 +4,22 @@
#pragma once
#include <functional>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <type_traits>
#include <vector>
#include <boost/optional.hpp>
#include "common/common_types.h"
#include "core/file_sys/vfs_types.h"
namespace FileSys {
class VfsDirectory;
class VfsFile;
class VfsFilesystem;
enum class Mode : u32;
// Convenience typedefs to use Vfs* interfaces
using VirtualFilesystem = std::shared_ptr<VfsFilesystem>;
using VirtualDir = std::shared_ptr<VfsDirectory>;
using VirtualFile = std::shared_ptr<VfsFile>;
// An enumeration representing what can be at the end of a path in a VfsFilesystem
enum class VfsEntryType {
None,
@@ -111,8 +105,8 @@ public:
// into file. Returns number of bytes successfully written.
virtual std::size_t Write(const u8* data, std::size_t length, std::size_t offset = 0) = 0;
// Reads exactly one byte at the offset provided, returning boost::none on error.
virtual boost::optional<u8> ReadByte(std::size_t offset = 0) const;
// Reads exactly one byte at the offset provided, returning std::nullopt on error.
virtual std::optional<u8> ReadByte(std::size_t offset = 0) const;
// Reads size bytes starting at offset in file into a vector.
virtual std::vector<u8> ReadBytes(std::size_t size, std::size_t offset = 0) const;
// Reads all the bytes from the file into a vector. Equivalent to 'file->Read(file->GetSize(),
@@ -270,36 +264,8 @@ public:
// item name -> type.
virtual std::map<std::string, VfsEntryType, std::less<>> GetEntries() const;
// Interprets the file with name file instead as a directory of type directory.
// The directory must have a constructor that takes a single argument of type
// std::shared_ptr<VfsFile>. Allows to reinterpret container files (i.e NCA, zip, XCI, etc) as a
// subdirectory in one call.
template <typename Directory>
bool InterpretAsDirectory(std::string_view file) {
auto file_p = GetFile(file);
if (file_p == nullptr) {
return false;
}
return ReplaceFileWithSubdirectory(file_p, std::make_shared<Directory>(file_p));
}
bool InterpretAsDirectory(const std::function<VirtualDir(VirtualFile)>& function,
const std::string& file) {
auto file_p = GetFile(file);
if (file_p == nullptr)
return false;
return ReplaceFileWithSubdirectory(file_p, function(file_p));
}
// Returns the full path of this directory as a string, recursively
virtual std::string GetFullPath() const;
protected:
// Backend for InterpretAsDirectory.
// Removes all references to file and adds a reference to dir in the directory's implementation.
virtual bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) = 0;
};
// A convenience partial-implementation of VfsDirectory that stubs out methods that should only work

View File

@@ -126,7 +126,4 @@ bool LayeredVfsDirectory::Rename(std::string_view name_) {
return true;
}
bool LayeredVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
return false;
}
} // namespace FileSys

View File

@@ -39,9 +39,6 @@ public:
bool DeleteFile(std::string_view name) override;
bool Rename(std::string_view name) override;
protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
private:
std::vector<VirtualDir> dirs;
std::string name;

View File

@@ -57,11 +57,11 @@ std::size_t OffsetVfsFile::Write(const u8* data, std::size_t length, std::size_t
return file->Write(data, TrimToFit(length, r_offset), offset + r_offset);
}
boost::optional<u8> OffsetVfsFile::ReadByte(std::size_t r_offset) const {
std::optional<u8> OffsetVfsFile::ReadByte(std::size_t r_offset) const {
if (r_offset < size)
return file->ReadByte(offset + r_offset);
return boost::none;
return {};
}
std::vector<u8> OffsetVfsFile::ReadBytes(std::size_t r_size, std::size_t r_offset) const {

View File

@@ -29,7 +29,7 @@ public:
bool IsReadable() const 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;
boost::optional<u8> ReadByte(std::size_t offset) const override;
std::optional<u8> ReadByte(std::size_t offset) const override;
std::vector<u8> ReadBytes(std::size_t size, std::size_t offset) const override;
std::vector<u8> ReadAllBytes() const override;
bool WriteByte(u8 data, std::size_t offset) override;

View File

@@ -430,7 +430,4 @@ std::map<std::string, VfsEntryType, std::less<>> RealVfsDirectory::GetEntries()
return out;
}
bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
return false;
}
} // namespace FileSys

View File

@@ -100,9 +100,6 @@ public:
std::string GetFullPath() const override;
std::map<std::string, VfsEntryType, std::less<>> GetEntries() const override;
protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
private:
RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, Mode perms = Mode::Read);

View File

@@ -53,10 +53,10 @@ public:
return 0;
}
boost::optional<u8> ReadByte(std::size_t offset) const override {
std::optional<u8> ReadByte(std::size_t offset) const override {
if (offset < size)
return value;
return boost::none;
return {};
}
std::vector<u8> ReadBytes(std::size_t length, std::size_t offset) const override {

View File

@@ -0,0 +1,21 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
namespace FileSys {
class VfsDirectory;
class VfsFile;
class VfsFilesystem;
// Declarations for Vfs* pointer types
using VirtualDir = std::shared_ptr<VfsDirectory>;
using VirtualFile = std::shared_ptr<VfsFile>;
using VirtualFilesystem = std::shared_ptr<VfsFilesystem>;
} // namespace FileSys

View File

@@ -132,11 +132,4 @@ void VectorVfsDirectory::AddFile(VirtualFile file) {
void VectorVfsDirectory::AddDirectory(VirtualDir dir) {
dirs.push_back(std::move(dir));
}
bool VectorVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
if (!DeleteFile(file->GetName()))
return false;
dirs.emplace_back(std::move(dir));
return true;
}
} // namespace FileSys

View File

@@ -57,9 +57,6 @@ public:
virtual void AddFile(VirtualFile file);
virtual void AddDirectory(VirtualDir dir);
protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
private:
std::vector<VirtualFile> files;
std::vector<VirtualDir> dirs;

View File

@@ -163,7 +163,4 @@ std::shared_ptr<VfsDirectory> NAX::GetParentDirectory() const {
return file->GetContainingDirectory();
}
bool NAX::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
return false;
}
} // namespace FileSys

View File

@@ -51,9 +51,6 @@ public:
std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
private:
Loader::ResultStatus Parse(std::string_view path);

View File

@@ -207,7 +207,7 @@ void RegisterModule(std::string name, VAddr beg, VAddr end, bool add_elf_ext) {
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();
const auto& threads = Core::System::GetInstance().Scheduler(core).GetThreadList();
for (auto& thread : threads) {
if (thread->GetThreadID() == static_cast<u32>(id)) {
current_core = core;
@@ -597,7 +597,7 @@ static void HandleQuery() {
} 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();
const auto& threads = Core::System::GetInstance().Scheduler(core).GetThreadList();
for (const auto& thread : threads) {
val += fmt::format("{:x}", thread->GetThreadID());
val += ",";
@@ -612,7 +612,7 @@ static void HandleQuery() {
buffer += "l<?xml version=\"1.0\"?>";
buffer += "<threads>";
for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList();
const auto& threads = Core::System::GetInstance().Scheduler(core).GetThreadList();
for (const auto& thread : threads) {
buffer +=
fmt::format(R"*(<thread id="{:x}" core="{:d}" name="Thread {:x}"></thread>)*",

View File

@@ -14,11 +14,6 @@ namespace IPC {
/// Size of the command buffer area, in 32-bit words.
constexpr std::size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32);
// These errors are commonly returned by invalid IPC translations, so alias them here for
// convenience.
// TODO(yuriks): These will probably go away once translation is implemented inside the kernel.
constexpr auto ERR_INVALID_HANDLE = Kernel::ERR_INVALID_HANDLE_OS;
enum class ControlCommand : u32 {
ConvertSessionToDomain = 0,
ConvertDomainToSession = 1,

View File

@@ -117,8 +117,7 @@ public:
AlignWithPadding();
const bool request_has_domain_header{context.GetDomainMessageHeader() != nullptr};
if (context.Session()->IsDomain() && request_has_domain_header) {
if (context.Session()->IsDomain() && context.HasDomainMessageHeader()) {
IPC::DomainMessageHeader domain_header{};
domain_header.num_objects = num_domain_objects;
PushRaw(domain_header);

View File

@@ -39,7 +39,7 @@ 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);
const auto& thread_list = scheduler->GetThreadList();
const auto& thread_list = scheduler.GetThreadList();
for (const auto& thread : thread_list) {
if (thread->GetArbiterWaitAddress() == arb_addr)

View File

@@ -10,11 +10,6 @@ namespace Kernel {
namespace ErrCodes {
enum {
// TODO(Subv): Remove these 3DS OS error codes.
SessionClosedByRemote = 26,
NoPendingSessions = 35,
InvalidBufferDescriptor = 48,
// Confirmed Switch OS error codes
MaxConnectionsReached = 7,
InvalidSize = 101,
@@ -22,9 +17,11 @@ enum {
HandleTableFull = 105,
InvalidMemoryState = 106,
InvalidMemoryPermissions = 108,
InvalidMemoryRange = 110,
InvalidThreadPriority = 112,
InvalidProcessorId = 113,
InvalidHandle = 114,
InvalidPointer = 115,
InvalidCombination = 116,
Timeout = 117,
SynchronizationCanceled = 118,
@@ -32,6 +29,7 @@ enum {
InvalidEnumValue = 120,
NoSuchEntry = 121,
AlreadyRegistered = 122,
SessionClosed = 123,
InvalidState = 125,
ResourceLimitExceeded = 132,
};
@@ -40,22 +38,19 @@ enum {
// WARNING: The kernel is quite inconsistent in it's usage of errors code. Make sure to always
// double check that the code matches before re-using the constant.
// TODO(bunnei): Replace -1 with correct errors for Switch OS
constexpr ResultCode ERR_HANDLE_TABLE_FULL(ErrorModule::Kernel, ErrCodes::HandleTableFull);
constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(-1);
constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(ErrorModule::Kernel, ErrCodes::SessionClosed);
constexpr ResultCode ERR_PORT_NAME_TOO_LONG(ErrorModule::Kernel, ErrCodes::TooLarge);
constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(ErrorModule::Kernel,
ErrCodes::MaxConnectionsReached);
constexpr ResultCode ERR_INVALID_ENUM_VALUE(ErrorModule::Kernel, ErrCodes::InvalidEnumValue);
constexpr ResultCode ERR_INVALID_ENUM_VALUE_FND(-1);
constexpr ResultCode ERR_INVALID_COMBINATION(-1);
constexpr ResultCode ERR_INVALID_COMBINATION_KERNEL(ErrorModule::Kernel,
ErrCodes::InvalidCombination);
constexpr ResultCode ERR_OUT_OF_MEMORY(-1);
constexpr ResultCode ERR_INVALID_ADDRESS(ErrorModule::Kernel, ErrCodes::InvalidAddress);
constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState);
constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel,
ErrCodes::InvalidMemoryPermissions);
constexpr ResultCode ERR_INVALID_MEMORY_RANGE(ErrorModule::Kernel, ErrCodes::InvalidMemoryRange);
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);
@@ -63,14 +58,8 @@ constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorModule::Kernel, ErrCodes::Alrea
constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState);
constexpr ResultCode ERR_INVALID_THREAD_PRIORITY(ErrorModule::Kernel,
ErrCodes::InvalidThreadPriority);
constexpr ResultCode ERR_INVALID_POINTER(-1);
constexpr ResultCode ERR_INVALID_OBJECT_ADDR(-1);
constexpr ResultCode ERR_NOT_AUTHORIZED(-1);
/// Alternate code returned instead of ERR_INVALID_HANDLE in some code paths.
constexpr ResultCode ERR_INVALID_HANDLE_OS(-1);
constexpr ResultCode ERR_INVALID_POINTER(ErrorModule::Kernel, ErrCodes::InvalidPointer);
constexpr ResultCode ERR_NOT_FOUND(ErrorModule::Kernel, ErrCodes::NoSuchEntry);
constexpr ResultCode RESULT_TIMEOUT(ErrorModule::Kernel, ErrCodes::Timeout);
/// Returned when Accept() is called on a port with no sessions to be accepted.
constexpr ResultCode ERR_NO_PENDING_SESSIONS(-1);
} // namespace Kernel

View File

@@ -77,7 +77,8 @@ HLERequestContext::HLERequestContext(SharedPtr<Kernel::ServerSession> server_ses
HLERequestContext::~HLERequestContext() = default;
void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
void HLERequestContext::ParseCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf,
bool incoming) {
IPC::RequestParser rp(src_cmdbuf);
command_header = std::make_shared<IPC::CommandHeader>(rp.PopRaw<IPC::CommandHeader>());
@@ -94,8 +95,6 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
rp.Skip(2, false);
}
if (incoming) {
auto& handle_table = Core::System::GetInstance().Kernel().HandleTable();
// Populate the object lists with the data in the IPC request.
for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_copy; ++handle) {
copy_objects.push_back(handle_table.GetGeneric(rp.Pop<Handle>()));
@@ -189,10 +188,9 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
rp.Skip(1, false); // The command is actually an u64, but we don't use the high part.
}
ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf,
Process& src_process,
HandleTable& src_table) {
ParseCommandBuffer(src_cmdbuf, true);
ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const HandleTable& handle_table,
u32_le* src_cmdbuf) {
ParseCommandBuffer(handle_table, src_cmdbuf, true);
if (command_header->type == IPC::CommandType::Close) {
// Close does not populate the rest of the IPC header
return RESULT_SUCCESS;
@@ -207,14 +205,17 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdb
return RESULT_SUCCESS;
}
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread) {
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(Thread& thread) {
auto& owner_process = *thread.GetOwnerProcess();
auto& handle_table = owner_process.GetHandleTable();
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf;
Memory::ReadBlock(*thread.GetOwnerProcess(), thread.GetTLSAddress(), dst_cmdbuf.data(),
Memory::ReadBlock(owner_process, 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
// the integrity and then copy it over to the target command buffer.
ParseCommandBuffer(cmd_buf.data(), false);
ParseCommandBuffer(handle_table, cmd_buf.data(), false);
// The data_size already includes the payload header, the padding and the domain header.
std::size_t size = data_payload_offset + command_header->data_size -
@@ -236,8 +237,6 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread)
ASSERT(copy_objects.size() == handle_descriptor_header->num_handles_to_copy);
ASSERT(move_objects.size() == handle_descriptor_header->num_handles_to_move);
auto& handle_table = Core::System::GetInstance().Kernel().HandleTable();
// We don't make a distinction between copy and move handles when translating since HLE
// services don't deal with handles directly. However, the guest applications might check
// for specific values in each of these descriptors.
@@ -268,7 +267,7 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread)
}
// Copy the translated command buffer back into the thread's command buffer area.
Memory::WriteBlock(*thread.GetOwnerProcess(), thread.GetTLSAddress(), dst_cmdbuf.data(),
Memory::WriteBlock(owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
dst_cmdbuf.size() * sizeof(u32));
return RESULT_SUCCESS;

View File

@@ -24,10 +24,10 @@ class ServiceFrameworkBase;
namespace Kernel {
class Domain;
class Event;
class HandleTable;
class HLERequestContext;
class Process;
class Event;
/**
* Interface implemented by HLE Session handlers.
@@ -126,13 +126,12 @@ public:
u64 timeout, WakeupCallback&& callback,
Kernel::SharedPtr<Kernel::Event> event = nullptr);
void ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming);
/// Populates this context with data from the requesting process/thread.
ResultCode PopulateFromIncomingCommandBuffer(u32_le* src_cmdbuf, Process& src_process,
HandleTable& src_table);
ResultCode PopulateFromIncomingCommandBuffer(const HandleTable& handle_table,
u32_le* src_cmdbuf);
/// Writes data from this context back to the requesting process/thread.
ResultCode WriteToOutgoingCommandBuffer(const Thread& thread);
ResultCode WriteToOutgoingCommandBuffer(Thread& thread);
u32_le GetCommand() const {
return command;
@@ -162,8 +161,12 @@ public:
return buffer_c_desciptors;
}
const std::shared_ptr<IPC::DomainMessageHeader>& GetDomainMessageHeader() const {
return domain_message_header;
const IPC::DomainMessageHeader* GetDomainMessageHeader() const {
return domain_message_header.get();
}
bool HasDomainMessageHeader() const {
return domain_message_header != nullptr;
}
/// Helper function to read a buffer using the appropriate buffer descriptor
@@ -255,6 +258,8 @@ public:
std::string Description() const;
private:
void ParseCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf, bool incoming);
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
SharedPtr<Kernel::ServerSession> server_session;
// TODO(yuriks): Check common usage of this and optimize size accordingly

View File

@@ -32,7 +32,7 @@ namespace Kernel {
*/
static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_late) {
const auto proper_handle = static_cast<Handle>(thread_handle);
auto& system = Core::System::GetInstance();
const auto& system = Core::System::GetInstance();
// Lock the global kernel mutex when we enter the kernel HLE.
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
@@ -90,7 +90,7 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_
/// The timer callback event, called when a timer is fired
static void TimerCallback(u64 timer_handle, int cycles_late) {
const auto proper_handle = static_cast<Handle>(timer_handle);
auto& system = Core::System::GetInstance();
const auto& system = Core::System::GetInstance();
SharedPtr<Timer> timer = system.Kernel().RetrieveTimerFromCallbackHandleTable(proper_handle);
if (timer == nullptr) {
@@ -118,7 +118,6 @@ struct KernelCore::Impl {
process_list.clear();
current_process = nullptr;
handle_table.Clear();
resource_limits.fill(nullptr);
thread_wakeup_callback_handle_table.Clear();
@@ -209,7 +208,6 @@ struct KernelCore::Impl {
std::vector<SharedPtr<Process>> process_list;
Process* current_process = nullptr;
Kernel::HandleTable handle_table;
std::array<SharedPtr<ResourceLimit>, 4> resource_limits;
/// The event type of the generic timer callback event
@@ -241,14 +239,6 @@ void KernelCore::Shutdown() {
impl->Shutdown();
}
Kernel::HandleTable& KernelCore::HandleTable() {
return impl->handle_table;
}
const Kernel::HandleTable& KernelCore::HandleTable() const {
return impl->handle_table;
}
SharedPtr<ResourceLimit> KernelCore::ResourceLimitForCategory(
ResourceLimitCategory category) const {
return impl->resource_limits.at(static_cast<std::size_t>(category));

View File

@@ -47,12 +47,6 @@ public:
/// Clears all resources in use by the kernel instance.
void Shutdown();
/// Provides a reference to the handle table.
Kernel::HandleTable& HandleTable();
/// Provides a const reference to the handle table.
const Kernel::HandleTable& HandleTable() const;
/// Retrieves a shared pointer to a ResourceLimit identified by the given category.
SharedPtr<ResourceLimit> ResourceLimitForCategory(ResourceLimitCategory category) const;

View File

@@ -6,8 +6,6 @@
#include <utility>
#include <vector>
#include <boost/range/algorithm_ext/erase.hpp>
#include "common/assert.h"
#include "core/core.h"
#include "core/hle/kernel/errors.h"

View File

@@ -25,7 +25,6 @@ bool Object::IsWaitable() const {
case HandleType::Process:
case HandleType::AddressArbiter:
case HandleType::ResourceLimit:
case HandleType::CodeSet:
case HandleType::ClientPort:
case HandleType::ClientSession:
return false;

View File

@@ -26,7 +26,6 @@ enum class HandleType : u32 {
AddressArbiter,
Timer,
ResourceLimit,
CodeSet,
ClientPort,
ServerPort,
ClientSession,

View File

@@ -20,13 +20,7 @@
namespace Kernel {
SharedPtr<CodeSet> CodeSet::Create(KernelCore& kernel, std::string name) {
SharedPtr<CodeSet> codeset(new CodeSet(kernel));
codeset->name = std::move(name);
return codeset;
}
CodeSet::CodeSet(KernelCore& kernel) : Object{kernel} {}
CodeSet::CodeSet() = default;
CodeSet::~CodeSet() = default;
SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) {
@@ -159,11 +153,11 @@ void Process::PrepareForTermination() {
}
};
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());
const 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());
}
/**
@@ -224,20 +218,26 @@ void Process::FreeTLSSlot(VAddr tls_address) {
tls_slots[tls_page].reset(tls_slot);
}
void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) {
void Process::LoadModule(CodeSet module_, VAddr base_addr) {
const auto MapSegment = [&](CodeSet::Segment& segment, VMAPermission permissions,
MemoryState memory_state) {
auto vma = vm_manager
.MapMemoryBlock(segment.addr + base_addr, module_->memory, segment.offset,
segment.size, memory_state)
.Unwrap();
const auto vma = vm_manager
.MapMemoryBlock(segment.addr + base_addr, module_.memory,
segment.offset, segment.size, memory_state)
.Unwrap();
vm_manager.Reprotect(vma, permissions);
};
// Map CodeSet segments
MapSegment(module_->CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic);
MapSegment(module_->RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable);
MapSegment(module_->DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable);
MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic);
MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable);
MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable);
// Clear instruction cache in CPU JIT
Core::System::GetInstance().ArmInterface(0).ClearInstructionCache();
Core::System::GetInstance().ArmInterface(1).ClearInstructionCache();
Core::System::GetInstance().ArmInterface(2).ClearInstructionCache();
Core::System::GetInstance().ArmInterface(3).ClearInstructionCache();
}
ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {

View File

@@ -13,6 +13,7 @@
#include <boost/container/static_vector.hpp>
#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/vm_manager.h"
@@ -24,6 +25,7 @@ class ProgramMetadata;
namespace Kernel {
class KernelCore;
class ResourceLimit;
struct AddressMapping {
// Address and size must be page-aligned
@@ -57,30 +59,33 @@ union ProcessFlags {
BitField<12, 1, u16> loaded_high; ///< Application loaded high (not at 0x00100000).
};
enum class ProcessStatus { Created, Running, Exited };
/**
* Indicates the status of a Process instance.
*
* @note These match the values as used by kernel,
* so new entries should only be added if RE
* shows that a new value has been introduced.
*/
enum class ProcessStatus {
Created,
CreatedWithDebuggerAttached,
Running,
WaitingForDebuggerToAttach,
DebuggerAttached,
Exiting,
Exited,
DebugBreak,
};
class ResourceLimit;
struct CodeSet final : public Object {
struct CodeSet final {
struct Segment {
std::size_t offset = 0;
VAddr addr = 0;
u32 size = 0;
};
static SharedPtr<CodeSet> Create(KernelCore& kernel, std::string name);
std::string GetTypeName() const override {
return "CodeSet";
}
std::string GetName() const override {
return name;
}
static const HandleType HANDLE_TYPE = HandleType::CodeSet;
HandleType GetHandleType() const override {
return HANDLE_TYPE;
}
explicit CodeSet();
~CodeSet();
Segment& CodeSegment() {
return segments[0];
@@ -109,14 +114,7 @@ struct CodeSet final : public Object {
std::shared_ptr<std::vector<u8>> memory;
std::array<Segment, 3> segments;
VAddr entrypoint;
/// Name of the process
std::string name;
private:
explicit CodeSet(KernelCore& kernel);
~CodeSet() override;
VAddr entrypoint = 0;
};
class Process final : public Object {
@@ -145,6 +143,16 @@ public:
return vm_manager;
}
/// Gets a reference to the process' handle table.
HandleTable& GetHandleTable() {
return handle_table;
}
/// Gets a const reference to the process' handle table.
const HandleTable& GetHandleTable() const {
return handle_table;
}
/// Gets the current status of the process
ProcessStatus GetStatus() const {
return status;
@@ -194,6 +202,16 @@ public:
return is_64bit_process;
}
/// Gets the total running time of the process instance in ticks.
u64 GetCPUTimeTicks() const {
return total_process_running_time_ticks;
}
/// Updates the total running time, adding the given ticks to it.
void UpdateCPUTimeTicks(u64 ticks) {
total_process_running_time_ticks += ticks;
}
/**
* Loads process-specifics configuration info with metadata provided
* by an executable.
@@ -219,7 +237,7 @@ public:
*/
void PrepareForTermination();
void LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr);
void LoadModule(CodeSet module_, VAddr base_addr);
///////////////////////////////////////////////////////////////////////////////////////////////
// Memory Management
@@ -297,6 +315,12 @@ private:
/// specified by metadata provided to the process during loading.
bool is_64bit_process = true;
/// Total running time for the process in ticks.
u64 total_process_running_time_ticks = 0;
/// Per-process handle table for storing created object handles in.
HandleTable handle_table;
std::string name;
};

View File

@@ -9,6 +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"
@@ -34,6 +35,10 @@ Thread* Scheduler::GetCurrentThread() const {
return current_thread.get();
}
u64 Scheduler::GetLastContextSwitchTicks() const {
return last_context_switch_time;
}
Thread* Scheduler::PopNextReadyThread() {
Thread* next = nullptr;
Thread* thread = GetCurrentThread();
@@ -54,7 +59,10 @@ Thread* Scheduler::PopNextReadyThread() {
}
void Scheduler::SwitchContext(Thread* new_thread) {
Thread* previous_thread = GetCurrentThread();
Thread* const previous_thread = GetCurrentThread();
Process* const previous_process = Core::CurrentProcess();
UpdateLastContextSwitchTime(previous_thread, previous_process);
// Save context for previous thread
if (previous_thread) {
@@ -78,8 +86,6 @@ void Scheduler::SwitchContext(Thread* new_thread) {
// Cancel any outstanding wakeup events for this thread
new_thread->CancelWakeupTimer();
auto* const previous_process = Core::CurrentProcess();
current_thread = new_thread;
ready_queue.remove(new_thread->GetPriority(), new_thread);
@@ -102,6 +108,22 @@ void Scheduler::SwitchContext(Thread* new_thread) {
}
}
void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) {
const u64 prev_switch_ticks = last_context_switch_time;
const u64 most_recent_switch_ticks = CoreTiming::GetTicks();
const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks;
if (thread != nullptr) {
thread->UpdateCPUTimeTicks(update_ticks);
}
if (process != nullptr) {
process->UpdateCPUTimeTicks(update_ticks);
}
last_context_switch_time = most_recent_switch_ticks;
}
void Scheduler::Reschedule() {
std::lock_guard<std::mutex> lock(scheduler_mutex);

View File

@@ -17,6 +17,8 @@ class ARM_Interface;
namespace Kernel {
class Process;
class Scheduler final {
public:
explicit Scheduler(Core::ARM_Interface& cpu_core);
@@ -31,6 +33,9 @@ public:
/// Gets the current running thread
Thread* GetCurrentThread() const;
/// Gets the timestamp for the last context switch in ticks.
u64 GetLastContextSwitchTicks() const;
/// Adds a new thread to the scheduler
void AddThread(SharedPtr<Thread> thread, u32 priority);
@@ -64,6 +69,19 @@ private:
*/
void SwitchContext(Thread* new_thread);
/**
* Called on every context switch to update the internal timestamp
* This also updates the running time ticks for the given thread and
* process using the following difference:
*
* ticks += most_recent_ticks - last_context_switch_ticks
*
* The internal tick timestamp for the scheduler is simply the
* most recent tick count retrieved. No special arithmetic is
* applied to it.
*/
void UpdateLastContextSwitchTime(Thread* thread, Process* process);
/// Lists all thread ids that aren't deleted/etc.
std::vector<SharedPtr<Thread>> thread_list;
@@ -73,6 +91,7 @@ private:
SharedPtr<Thread> current_thread = nullptr;
Core::ARM_Interface& cpu_core;
u64 last_context_switch_time = 0;
static std::mutex scheduler_mutex;
};

View File

@@ -18,7 +18,7 @@ ServerPort::~ServerPort() = default;
ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() {
if (pending_sessions.empty()) {
return ERR_NO_PENDING_SESSIONS;
return ERR_NOT_FOUND;
}
auto session = std::move(pending_sessions.back());
@@ -28,7 +28,7 @@ ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() {
bool ServerPort::ShouldWait(Thread* thread) const {
// If there are no pending sessions, we wait until a new one is added.
return pending_sessions.size() == 0;
return pending_sessions.empty();
}
void ServerPort::Acquire(Thread* thread) {

View File

@@ -63,7 +63,7 @@ void ServerSession::Acquire(Thread* thread) {
}
ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) {
auto& domain_message_header = context.GetDomainMessageHeader();
auto* const domain_message_header = context.GetDomainMessageHeader();
if (domain_message_header) {
// Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs
context.SetDomainRequestHandlers(domain_request_handlers);
@@ -107,12 +107,11 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {
// similar.
Kernel::HLERequestContext context(this);
u32* cmd_buf = (u32*)Memory::GetPointer(thread->GetTLSAddress());
context.PopulateFromIncomingCommandBuffer(cmd_buf, *Core::CurrentProcess(),
kernel.HandleTable());
context.PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf);
ResultCode result = RESULT_SUCCESS;
// If the session has been converted to a domain, handle the domain request
if (IsDomain() && context.GetDomainMessageHeader()) {
if (IsDomain() && context.HasDomainMessageHeader()) {
result = HandleDomainSyncRequest(context);
// If there is no domain header, the regular session handler is used
} else if (hle_handler != nullptr) {

View File

@@ -80,20 +80,19 @@ SharedPtr<SharedMemory> SharedMemory::CreateForApplet(
ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermission permissions,
MemoryPermission other_permissions) {
MemoryPermission own_other_permissions =
const MemoryPermission own_other_permissions =
target_process == owner_process ? this->permissions : this->other_permissions;
// Automatically allocated memory blocks can only be mapped with other_permissions = DontCare
if (base_address == 0 && other_permissions != MemoryPermission::DontCare) {
return ERR_INVALID_COMBINATION;
return ERR_INVALID_MEMORY_PERMISSIONS;
}
// Error out if the requested permissions don't match what the creator process allows.
if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) {
LOG_ERROR(Kernel, "cannot map id={}, address=0x{:X} name={}, permissions don't match",
GetObjectId(), address, name);
return ERR_INVALID_COMBINATION;
return ERR_INVALID_MEMORY_PERMISSIONS;
}
// Error out if the provided permissions are not compatible with what the creator process needs.

View File

@@ -8,6 +8,7 @@
#include <mutex>
#include <vector>
#include "common/alignment.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/microprofile.h"
@@ -36,8 +37,72 @@
namespace Kernel {
namespace {
constexpr bool Is4KBAligned(VAddr address) {
return (address & 0xFFF) == 0;
// Checks if address + size is greater than the given address
// This can return false if the size causes an overflow of a 64-bit type
// or if the given size is zero.
constexpr bool IsValidAddressRange(VAddr address, u64 size) {
return address + size > address;
}
// Checks if a given address range lies within a larger address range.
constexpr bool IsInsideAddressRange(VAddr address, u64 size, VAddr address_range_begin,
VAddr address_range_end) {
const VAddr end_address = address + size - 1;
return address_range_begin <= address && end_address <= address_range_end - 1;
}
bool IsInsideAddressSpace(const VMManager& vm, VAddr address, u64 size) {
return IsInsideAddressRange(address, size, vm.GetAddressSpaceBaseAddress(),
vm.GetAddressSpaceEndAddress());
}
bool IsInsideNewMapRegion(const VMManager& vm, VAddr address, u64 size) {
return IsInsideAddressRange(address, size, vm.GetNewMapRegionBaseAddress(),
vm.GetNewMapRegionEndAddress());
}
// Helper function that performs the common sanity checks for svcMapMemory
// and svcUnmapMemory. This is doable, as both functions perform their sanitizing
// in the same order.
ResultCode MapUnmapMemorySanityChecks(const VMManager& vm_manager, VAddr dst_addr, VAddr src_addr,
u64 size) {
if (!Common::Is4KBAligned(dst_addr) || !Common::Is4KBAligned(src_addr)) {
return ERR_INVALID_ADDRESS;
}
if (size == 0 || !Common::Is4KBAligned(size)) {
return ERR_INVALID_SIZE;
}
if (!IsValidAddressRange(dst_addr, size)) {
return ERR_INVALID_ADDRESS_STATE;
}
if (!IsValidAddressRange(src_addr, size)) {
return ERR_INVALID_ADDRESS_STATE;
}
if (!IsInsideAddressSpace(vm_manager, src_addr, size)) {
return ERR_INVALID_ADDRESS_STATE;
}
if (!IsInsideNewMapRegion(vm_manager, dst_addr, size)) {
return ERR_INVALID_MEMORY_RANGE;
}
const VAddr dst_end_address = dst_addr + size;
if (dst_end_address > vm_manager.GetHeapRegionBaseAddress() &&
vm_manager.GetHeapRegionEndAddress() > dst_addr) {
return ERR_INVALID_MEMORY_RANGE;
}
if (dst_end_address > vm_manager.GetMapRegionBaseAddress() &&
vm_manager.GetMapRegionEndAddress() > dst_addr) {
return ERR_INVALID_MEMORY_RANGE;
}
return RESULT_SUCCESS;
}
} // Anonymous namespace
@@ -69,15 +134,15 @@ static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
src_addr, size);
if (!Is4KBAligned(dst_addr) || !Is4KBAligned(src_addr)) {
return ERR_INVALID_ADDRESS;
auto* const current_process = Core::CurrentProcess();
const auto& vm_manager = current_process->VMManager();
const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size);
if (result != RESULT_SUCCESS) {
return result;
}
if (size == 0 || !Is4KBAligned(size)) {
return ERR_INVALID_SIZE;
}
return Core::CurrentProcess()->MirrorMemory(dst_addr, src_addr, size);
return current_process->MirrorMemory(dst_addr, src_addr, size);
}
/// Unmaps a region that was previously mapped with svcMapMemory
@@ -85,15 +150,15 @@ static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
src_addr, size);
if (!Is4KBAligned(dst_addr) || !Is4KBAligned(src_addr)) {
return ERR_INVALID_ADDRESS;
auto* const current_process = Core::CurrentProcess();
const auto& vm_manager = current_process->VMManager();
const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size);
if (result != RESULT_SUCCESS) {
return result;
}
if (size == 0 || !Is4KBAligned(size)) {
return ERR_INVALID_SIZE;
}
return Core::CurrentProcess()->UnmapMemory(dst_addr, src_addr, size);
return current_process->UnmapMemory(dst_addr, src_addr, size);
}
/// Connect to an OS service given the port name, returns the handle to the port to out
@@ -124,14 +189,15 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address
CASCADE_RESULT(client_session, client_port->Connect());
// Return the client session
CASCADE_RESULT(*out_handle, kernel.HandleTable().Create(client_session));
auto& handle_table = Core::CurrentProcess()->GetHandleTable();
CASCADE_RESULT(*out_handle, handle_table.Create(client_session));
return RESULT_SUCCESS;
}
/// Makes a blocking IPC call to an OS service.
static ResultCode SendSyncRequest(Handle handle) {
auto& kernel = Core::System::GetInstance().Kernel();
SharedPtr<ClientSession> session = kernel.HandleTable().Get<ClientSession>(handle);
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
SharedPtr<ClientSession> session = handle_table.Get<ClientSession>(handle);
if (!session) {
LOG_ERROR(Kernel_SVC, "called with invalid handle=0x{:08X}", handle);
return ERR_INVALID_HANDLE;
@@ -150,8 +216,8 @@ static ResultCode SendSyncRequest(Handle handle) {
static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) {
LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
auto& kernel = Core::System::GetInstance().Kernel();
const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle);
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
if (!thread) {
return ERR_INVALID_HANDLE;
}
@@ -164,8 +230,8 @@ static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) {
static ResultCode GetProcessId(u32* process_id, Handle process_handle) {
LOG_TRACE(Kernel_SVC, "called process=0x{:08X}", process_handle);
auto& kernel = Core::System::GetInstance().Kernel();
const SharedPtr<Process> process = kernel.HandleTable().Get<Process>(process_handle);
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
const SharedPtr<Process> process = handle_table.Get<Process>(process_handle);
if (!process) {
return ERR_INVALID_HANDLE;
}
@@ -208,11 +274,11 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
using ObjectPtr = Thread::ThreadWaitObjects::value_type;
Thread::ThreadWaitObjects objects(handle_count);
auto& kernel = Core::System::GetInstance().Kernel();
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
for (u64 i = 0; i < handle_count; ++i) {
const Handle handle = Memory::Read32(handles_address + i * sizeof(Handle));
const auto object = kernel.HandleTable().Get<WaitObject>(handle);
const auto object = handle_table.Get<WaitObject>(handle);
if (object == nullptr) {
return ERR_INVALID_HANDLE;
@@ -260,8 +326,8 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
static ResultCode CancelSynchronization(Handle thread_handle) {
LOG_TRACE(Kernel_SVC, "called thread=0x{:X}", thread_handle);
auto& kernel = Core::System::GetInstance().Kernel();
const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle);
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
if (!thread) {
return ERR_INVALID_HANDLE;
}
@@ -285,7 +351,11 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr,
return ERR_INVALID_ADDRESS_STATE;
}
auto& handle_table = Core::System::GetInstance().Kernel().HandleTable();
if (!Common::IsWordAligned(mutex_addr)) {
return ERR_INVALID_ADDRESS;
}
auto& handle_table = Core::CurrentProcess()->GetHandleTable();
return Mutex::TryAcquire(handle_table, mutex_addr, holding_thread_handle,
requesting_thread_handle);
}
@@ -298,30 +368,114 @@ static ResultCode ArbitrateUnlock(VAddr mutex_addr) {
return ERR_INVALID_ADDRESS_STATE;
}
if (!Common::IsWordAligned(mutex_addr)) {
return ERR_INVALID_ADDRESS;
}
return Mutex::Release(mutex_addr);
}
enum class BreakType : u32 {
Panic = 0,
AssertionFailed = 1,
PreNROLoad = 3,
PostNROLoad = 4,
PreNROUnload = 5,
PostNROUnload = 6,
};
struct BreakReason {
union {
u64 raw;
BitField<31, 1, u64> dont_kill_application;
u32 raw;
BitField<0, 30, BreakType> break_type;
BitField<31, 1, u32> signal_debugger;
};
};
/// Break program execution
static void Break(u64 reason, u64 info1, u64 info2) {
static void Break(u32 reason, u64 info1, u64 info2) {
BreakReason break_reason{reason};
if (break_reason.dont_kill_application) {
LOG_ERROR(
bool has_dumped_buffer{};
const auto handle_debug_buffer = [&](VAddr addr, u64 sz) {
if (sz == 0 || addr == 0 || has_dumped_buffer) {
return;
}
// This typically is an error code so we're going to assume this is the case
if (sz == sizeof(u32)) {
LOG_CRITICAL(Debug_Emulated, "debug_buffer_err_code={:X}", Memory::Read32(addr));
} else {
// We don't know what's in here so we'll hexdump it
std::vector<u8> debug_buffer(sz);
Memory::ReadBlock(addr, debug_buffer.data(), sz);
std::string hexdump;
for (std::size_t i = 0; i < debug_buffer.size(); i++) {
hexdump += fmt::format("{:02X} ", debug_buffer[i]);
if (i != 0 && i % 16 == 0) {
hexdump += '\n';
}
}
LOG_CRITICAL(Debug_Emulated, "debug_buffer=\n{}", hexdump);
}
has_dumped_buffer = true;
};
switch (break_reason.break_type) {
case BreakType::Panic:
LOG_CRITICAL(Debug_Emulated, "Signalling debugger, PANIC! info1=0x{:016X}, info2=0x{:016X}",
info1, info2);
handle_debug_buffer(info1, info2);
break;
case BreakType::AssertionFailed:
LOG_CRITICAL(Debug_Emulated,
"Signalling debugger, Assertion failed! info1=0x{:016X}, info2=0x{:016X}",
info1, info2);
handle_debug_buffer(info1, info2);
break;
case BreakType::PreNROLoad:
LOG_WARNING(
Debug_Emulated,
"Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
reason, info1, info2);
} else {
"Signalling debugger, Attempting to load an NRO at 0x{:016X} with size 0x{:016X}",
info1, info2);
break;
case BreakType::PostNROLoad:
LOG_WARNING(Debug_Emulated,
"Signalling debugger, Loaded an NRO at 0x{:016X} with size 0x{:016X}", info1,
info2);
break;
case BreakType::PreNROUnload:
LOG_WARNING(
Debug_Emulated,
"Signalling debugger, Attempting to unload an NRO at 0x{:016X} with size 0x{:016X}",
info1, info2);
break;
case BreakType::PostNROUnload:
LOG_WARNING(Debug_Emulated,
"Signalling debugger, Unloaded an NRO at 0x{:016X} with size 0x{:016X}", info1,
info2);
break;
default:
LOG_WARNING(
Debug_Emulated,
"Signalling debugger, Unknown break reason {}, info1=0x{:016X}, info2=0x{:016X}",
static_cast<u32>(break_reason.break_type.Value()), info1, info2);
handle_debug_buffer(info1, info2);
break;
}
if (!break_reason.signal_debugger) {
LOG_CRITICAL(
Debug_Emulated,
"Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
reason, info1, info2);
handle_debug_buffer(info1, info2);
ASSERT(false);
Core::CurrentProcess()->PrepareForTermination();
// Kill the current thread
GetCurrentThread()->Stop();
Core::System::GetInstance().PrepareReschedule();
}
}
@@ -341,6 +495,37 @@ 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);
enum class GetInfoType : u64 {
// 1.0.0+
AllowedCpuIdBitmask = 0,
AllowedThreadPrioBitmask = 1,
MapRegionBaseAddr = 2,
MapRegionSize = 3,
HeapRegionBaseAddr = 4,
HeapRegionSize = 5,
TotalMemoryUsage = 6,
TotalHeapUsage = 7,
IsCurrentProcessBeingDebugged = 8,
ResourceHandleLimit = 9,
IdleTickCount = 10,
RandomEntropy = 11,
PerformanceCounter = 0xF0000002,
// 2.0.0+
ASLRRegionBaseAddr = 12,
ASLRRegionSize = 13,
NewMapRegionBaseAddr = 14,
NewMapRegionSize = 15,
// 3.0.0+
IsVirtualAddressMemoryEnabled = 16,
PersonalMmHeapUsage = 17,
TitleId = 18,
// 4.0.0+
PrivilegedProcessId = 19,
// 5.0.0+
UserExceptionContextAddr = 20,
ThreadTickCount = 0xF0000002,
};
const auto* current_process = Core::CurrentProcess();
const auto& vm_manager = current_process->VMManager();
@@ -375,25 +560,12 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
case GetInfoType::RandomEntropy:
*result = 0;
break;
case GetInfoType::AddressSpaceBaseAddr:
*result = vm_manager.GetCodeRegionBaseAddress();
case GetInfoType::ASLRRegionBaseAddr:
*result = vm_manager.GetASLRRegionBaseAddress();
break;
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;
}
case GetInfoType::ASLRRegionSize:
*result = vm_manager.GetASLRRegionSize();
break;
}
case GetInfoType::NewMapRegionBaseAddr:
*result = vm_manager.GetNewMapRegionBaseAddress();
break;
@@ -416,6 +588,36 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
"(STUBBED) Attempted to query user exception context address, returned 0");
*result = 0;
break;
case GetInfoType::ThreadTickCount: {
constexpr u64 num_cpus = 4;
if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) {
return ERR_INVALID_COMBINATION_KERNEL;
}
const auto thread =
current_process->GetHandleTable().Get<Thread>(static_cast<Handle>(handle));
if (!thread) {
return ERR_INVALID_HANDLE;
}
const auto& system = Core::System::GetInstance();
const auto& scheduler = system.CurrentScheduler();
const auto* const current_thread = scheduler.GetCurrentThread();
const bool same_thread = current_thread == thread;
const u64 prev_ctx_ticks = scheduler.GetLastContextSwitchTicks();
u64 out_ticks = 0;
if (same_thread && info_sub_id == 0xFFFFFFFFFFFFFFFF) {
const u64 thread_ticks = current_thread->GetTotalCPUTimeTicks();
out_ticks = thread_ticks + (CoreTiming::GetTicks() - prev_ctx_ticks);
} else if (same_thread && info_sub_id == system.CurrentCoreIndex()) {
out_ticks = CoreTiming::GetTicks() - prev_ctx_ticks;
}
*result = out_ticks;
break;
}
default:
UNIMPLEMENTED();
}
@@ -433,13 +635,12 @@ static ResultCode SetThreadActivity(Handle handle, u32 unknown) {
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);
const auto* current_process = Core::CurrentProcess();
const SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
if (!thread) {
return ERR_INVALID_HANDLE;
}
const auto* current_process = Core::CurrentProcess();
if (thread->GetOwnerProcess() != current_process) {
return ERR_INVALID_HANDLE;
}
@@ -465,10 +666,11 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) {
/// Gets the priority for the specified thread
static ResultCode GetThreadPriority(u32* priority, Handle handle) {
auto& kernel = Core::System::GetInstance().Kernel();
const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(handle);
if (!thread)
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
const SharedPtr<Thread> thread = handle_table.Get<Thread>(handle);
if (!thread) {
return ERR_INVALID_HANDLE;
}
*priority = thread->GetPriority();
return RESULT_SUCCESS;
@@ -480,16 +682,18 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) {
return ERR_INVALID_THREAD_PRIORITY;
}
auto& kernel = Core::System::GetInstance().Kernel();
SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(handle);
if (!thread)
return ERR_INVALID_HANDLE;
const auto* const current_process = Core::CurrentProcess();
// Note: The kernel uses the current process's resource limit instead of
// the one from the thread owner's resource limit.
const ResourceLimit& resource_limit = Core::CurrentProcess()->GetResourceLimit();
const ResourceLimit& resource_limit = current_process->GetResourceLimit();
if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) {
return ERR_NOT_AUTHORIZED;
return ERR_INVALID_THREAD_PRIORITY;
}
SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
if (!thread) {
return ERR_INVALID_HANDLE;
}
thread->SetPriority(priority);
@@ -510,14 +714,18 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
"called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}",
shared_memory_handle, addr, size, permissions);
if (!Is4KBAligned(addr)) {
if (!Common::Is4KBAligned(addr)) {
return ERR_INVALID_ADDRESS;
}
if (size == 0 || !Is4KBAligned(size)) {
if (size == 0 || !Common::Is4KBAligned(size)) {
return ERR_INVALID_SIZE;
}
if (!IsValidAddressRange(addr, size)) {
return ERR_INVALID_ADDRESS_STATE;
}
const auto permissions_type = static_cast<MemoryPermission>(permissions);
if (permissions_type != MemoryPermission::Read &&
permissions_type != MemoryPermission::ReadWrite) {
@@ -525,46 +733,61 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
return ERR_INVALID_MEMORY_PERMISSIONS;
}
auto& kernel = Core::System::GetInstance().Kernel();
auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle);
auto* const current_process = Core::CurrentProcess();
auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle);
if (!shared_memory) {
return ERR_INVALID_HANDLE;
}
return shared_memory->Map(Core::CurrentProcess(), addr, permissions_type,
MemoryPermission::DontCare);
const auto& vm_manager = current_process->VMManager();
if (!vm_manager.IsWithinASLRRegion(addr, size)) {
return ERR_INVALID_MEMORY_RANGE;
}
return shared_memory->Map(current_process, addr, permissions_type, MemoryPermission::DontCare);
}
static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) {
LOG_WARNING(Kernel_SVC, "called, shared_memory_handle=0x{:08X}, addr=0x{:X}, size=0x{:X}",
shared_memory_handle, addr, size);
if (!Is4KBAligned(addr)) {
if (!Common::Is4KBAligned(addr)) {
return ERR_INVALID_ADDRESS;
}
if (size == 0 || !Is4KBAligned(size)) {
if (size == 0 || !Common::Is4KBAligned(size)) {
return ERR_INVALID_SIZE;
}
auto& kernel = Core::System::GetInstance().Kernel();
auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle);
if (!IsValidAddressRange(addr, size)) {
return ERR_INVALID_ADDRESS_STATE;
}
return shared_memory->Unmap(Core::CurrentProcess(), addr);
auto* const current_process = Core::CurrentProcess();
auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle);
if (!shared_memory) {
return ERR_INVALID_HANDLE;
}
const auto& vm_manager = current_process->VMManager();
if (!vm_manager.IsWithinASLRRegion(addr, size)) {
return ERR_INVALID_MEMORY_RANGE;
}
return shared_memory->Unmap(current_process, addr);
}
/// Query process memory
static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_info*/,
Handle process_handle, u64 addr) {
auto& kernel = Core::System::GetInstance().Kernel();
SharedPtr<Process> process = kernel.HandleTable().Get<Process>(process_handle);
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
SharedPtr<Process> process = handle_table.Get<Process>(process_handle);
if (!process) {
return ERR_INVALID_HANDLE;
}
auto vma = process->VMManager().FindVMA(addr);
memory_info->attributes = 0;
if (vma == Core::CurrentProcess()->VMManager().vma_map.end()) {
if (vma == process->VMManager().vma_map.end()) {
memory_info->base_address = 0;
memory_info->permission = static_cast<u32>(VMAPermission::None);
memory_info->size = 0;
@@ -605,20 +828,19 @@ static void ExitProcess() {
/// Creates a new thread
static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, VAddr stack_top,
u32 priority, s32 processor_id) {
std::string name = fmt::format("thread-{:X}", entry_point);
if (priority > THREADPRIO_LOWEST) {
return ERR_INVALID_THREAD_PRIORITY;
}
const ResourceLimit& resource_limit = Core::CurrentProcess()->GetResourceLimit();
auto* const current_process = Core::CurrentProcess();
const ResourceLimit& resource_limit = current_process->GetResourceLimit();
if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) {
return ERR_NOT_AUTHORIZED;
return ERR_INVALID_THREAD_PRIORITY;
}
if (processor_id == THREADPROCESSORID_DEFAULT) {
// Set the target CPU to the one specified in the process' exheader.
processor_id = Core::CurrentProcess()->GetDefaultProcessorID();
processor_id = current_process->GetDefaultProcessorID();
ASSERT(processor_id != THREADPROCESSORID_DEFAULT);
}
@@ -633,11 +855,13 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
return ERR_INVALID_PROCESSOR_ID;
}
const std::string name = fmt::format("thread-{:X}", entry_point);
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()));
const auto new_guest_handle = kernel.HandleTable().Create(thread);
*current_process));
const auto new_guest_handle = current_process->GetHandleTable().Create(thread);
if (new_guest_handle.Failed()) {
return new_guest_handle.Code();
}
@@ -658,8 +882,8 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
static ResultCode StartThread(Handle thread_handle) {
LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
auto& kernel = Core::System::GetInstance().Kernel();
const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle);
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
if (!thread) {
return ERR_INVALID_HANDLE;
}
@@ -706,8 +930,8 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var
"called mutex_addr={:X}, condition_variable_addr={:X}, thread_handle=0x{:08X}, timeout={}",
mutex_addr, condition_variable_addr, thread_handle, nano_seconds);
auto& kernel = Core::System::GetInstance().Kernel();
SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle);
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
ASSERT(thread);
CASCADE_CODE(Mutex::Release(mutex_addr));
@@ -736,7 +960,7 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
std::vector<SharedPtr<Thread>>& waiting_threads,
VAddr condvar_addr) {
const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
const auto& thread_list = scheduler->GetThreadList();
const auto& thread_list = scheduler.GetThreadList();
for (const auto& thread : thread_list) {
if (thread->GetCondVarWaitAddress() == condvar_addr)
@@ -818,9 +1042,9 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
mutex_val | Mutex::MutexHasWaitersFlag));
// The mutex is already owned by some other thread, make this thread wait on it.
auto& kernel = Core::System::GetInstance().Kernel();
Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask);
auto owner = kernel.HandleTable().Get<Thread>(owner_handle);
const Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask);
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
auto owner = handle_table.Get<Thread>(owner_handle);
ASSERT(owner);
ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex);
thread->InvalidateWakeupCallback();
@@ -899,16 +1123,16 @@ static u64 GetSystemTick() {
static ResultCode CloseHandle(Handle handle) {
LOG_TRACE(Kernel_SVC, "Closing handle 0x{:08X}", handle);
auto& kernel = Core::System::GetInstance().Kernel();
return kernel.HandleTable().Close(handle);
auto& handle_table = Core::CurrentProcess()->GetHandleTable();
return handle_table.Close(handle);
}
/// Reset an event
static ResultCode ResetSignal(Handle handle) {
LOG_WARNING(Kernel_SVC, "(STUBBED) called handle 0x{:08X}", handle);
auto& kernel = Core::System::GetInstance().Kernel();
auto event = kernel.HandleTable().Get<Event>(handle);
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
auto event = handle_table.Get<Event>(handle);
ASSERT(event != nullptr);
@@ -927,8 +1151,8 @@ static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32
static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask) {
LOG_TRACE(Kernel_SVC, "called, handle=0x{:08X}", thread_handle);
auto& kernel = Core::System::GetInstance().Kernel();
const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle);
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
if (!thread) {
return ERR_INVALID_HANDLE;
}
@@ -943,8 +1167,8 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:16X}, core=0x{:X}", thread_handle,
mask, core);
auto& kernel = Core::System::GetInstance().Kernel();
const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(thread_handle);
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
if (!thread) {
return ERR_INVALID_HANDLE;
}
@@ -1005,7 +1229,7 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss
}
auto& kernel = Core::System::GetInstance().Kernel();
auto& handle_table = kernel.HandleTable();
auto& handle_table = Core::CurrentProcess()->GetHandleTable();
auto shared_mem_handle =
SharedMemory::Create(kernel, handle_table.Get<Process>(KernelHandle::CurrentProcess), size,
local_perms, remote_perms);
@@ -1017,14 +1241,39 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss
static ResultCode ClearEvent(Handle handle) {
LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle);
auto& kernel = Core::System::GetInstance().Kernel();
SharedPtr<Event> evt = kernel.HandleTable().Get<Event>(handle);
if (evt == nullptr)
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
SharedPtr<Event> evt = handle_table.Get<Event>(handle);
if (evt == nullptr) {
return ERR_INVALID_HANDLE;
}
evt->Clear();
return RESULT_SUCCESS;
}
static ResultCode GetProcessInfo(u64* out, Handle process_handle, u32 type) {
LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, type=0x{:X}", process_handle, type);
// This function currently only allows retrieving a process' status.
enum class InfoType {
Status,
};
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
const auto process = handle_table.Get<Process>(process_handle);
if (!process) {
return ERR_INVALID_HANDLE;
}
const auto info_type = static_cast<InfoType>(type);
if (info_type != InfoType::Status) {
return ERR_INVALID_ENUM_VALUE;
}
*out = static_cast<u64>(process->GetStatus());
return RESULT_SUCCESS;
}
namespace {
struct FunctionDef {
using Func = void();
@@ -1160,7 +1409,7 @@ static const FunctionDef SVC_Table[] = {
{0x79, nullptr, "CreateProcess"},
{0x7A, nullptr, "StartProcess"},
{0x7B, nullptr, "TerminateProcess"},
{0x7C, nullptr, "GetProcessInfo"},
{0x7C, SvcWrap<GetProcessInfo>, "GetProcessInfo"},
{0x7D, nullptr, "CreateResourceLimit"},
{0x7E, nullptr, "SetResourceLimitLimitValue"},
{0x7F, nullptr, "CallSecureMonitor"},

View File

@@ -24,37 +24,6 @@ struct PageInfo {
u64 flags;
};
/// Values accepted by svcGetInfo
enum class GetInfoType : u64 {
// 1.0.0+
AllowedCpuIdBitmask = 0,
AllowedThreadPrioBitmask = 1,
MapRegionBaseAddr = 2,
MapRegionSize = 3,
HeapRegionBaseAddr = 4,
HeapRegionSize = 5,
TotalMemoryUsage = 6,
TotalHeapUsage = 7,
IsCurrentProcessBeingDebugged = 8,
ResourceHandleLimit = 9,
IdleTickCount = 10,
RandomEntropy = 11,
PerformanceCounter = 0xF0000002,
// 2.0.0+
AddressSpaceBaseAddr = 12,
AddressSpaceSize = 13,
NewMapRegionBaseAddr = 14,
NewMapRegionSize = 15,
// 3.0.0+
IsVirtualAddressMemoryEnabled = 16,
PersonalMmHeapUsage = 17,
TitleId = 18,
// 4.0.0+
PrivilegedProcessId = 19,
// 5.0.0+
UserExceptionContextAddr = 20,
};
void CallSVC(u32 immediate);
} // namespace Kernel

View File

@@ -35,18 +35,18 @@ void SvcWrap() {
template <ResultCode func(u32)>
void SvcWrap() {
FuncReturn(func((u32)Param(0)).raw);
FuncReturn(func(static_cast<u32>(Param(0))).raw);
}
template <ResultCode func(u32, u32)>
void SvcWrap() {
FuncReturn(func((u32)Param(0), (u32)Param(1)).raw);
FuncReturn(func(static_cast<u32>(Param(0)), static_cast<u32>(Param(1))).raw);
}
template <ResultCode func(u32*, u32)>
void SvcWrap() {
u32 param_1 = 0;
u32 retval = func(&param_1, (u32)Param(1)).raw;
u32 retval = func(&param_1, static_cast<u32>(Param(1))).raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
}
@@ -61,7 +61,7 @@ void SvcWrap() {
template <ResultCode func(u64, s32)>
void SvcWrap() {
FuncReturn(func(Param(0), (s32)Param(1)).raw);
FuncReturn(func(Param(0), static_cast<s32>(Param(1))).raw);
}
template <ResultCode func(u64, u32)>
@@ -77,21 +77,29 @@ void SvcWrap() {
FuncReturn(retval);
}
template <ResultCode func(u64*, u32, u32)>
void SvcWrap() {
u64 param_1 = 0;
u32 retval = func(&param_1, static_cast<u32>(Param(1)), static_cast<u32>(Param(2))).raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
}
template <ResultCode func(u32, u64)>
void SvcWrap() {
FuncReturn(func((u32)(Param(0) & 0xFFFFFFFF), Param(1)).raw);
FuncReturn(func(static_cast<u32>(Param(0)), Param(1)).raw);
}
template <ResultCode func(u32, u32, u64)>
void SvcWrap() {
FuncReturn(func((u32)(Param(0) & 0xFFFFFFFF), (u32)(Param(1) & 0xFFFFFFFF), Param(2)).raw);
FuncReturn(func(static_cast<u32>(Param(0)), static_cast<u32>(Param(1)), Param(2)).raw);
}
template <ResultCode func(u32, u32*, u64*)>
void SvcWrap() {
u32 param_1 = 0;
u64 param_2 = 0;
ResultCode retval = func((u32)(Param(2) & 0xFFFFFFFF), &param_1, &param_2);
ResultCode retval = func(static_cast<u32>(Param(2)), &param_1, &param_2);
Core::CurrentArmInterface().SetReg(1, param_1);
Core::CurrentArmInterface().SetReg(2, param_2);
FuncReturn(retval.raw);
@@ -100,12 +108,12 @@ void SvcWrap() {
template <ResultCode func(u64, u64, u32, u32)>
void SvcWrap() {
FuncReturn(
func(Param(0), Param(1), (u32)(Param(3) & 0xFFFFFFFF), (u32)(Param(3) & 0xFFFFFFFF)).raw);
func(Param(0), Param(1), static_cast<u32>(Param(3)), static_cast<u32>(Param(3))).raw);
}
template <ResultCode func(u32, u64, u32)>
void SvcWrap() {
FuncReturn(func((u32)Param(0), Param(1), (u32)Param(2)).raw);
FuncReturn(func(static_cast<u32>(Param(0)), Param(1), static_cast<u32>(Param(2))).raw);
}
template <ResultCode func(u64, u64, u64)>
@@ -115,25 +123,28 @@ void SvcWrap() {
template <ResultCode func(u32, u64, u64, u32)>
void SvcWrap() {
FuncReturn(func((u32)Param(0), Param(1), Param(2), (u32)Param(3)).raw);
FuncReturn(
func(static_cast<u32>(Param(0)), Param(1), Param(2), static_cast<u32>(Param(3))).raw);
}
template <ResultCode func(u32, u64, u64)>
void SvcWrap() {
FuncReturn(func((u32)Param(0), Param(1), Param(2)).raw);
FuncReturn(func(static_cast<u32>(Param(0)), Param(1), Param(2)).raw);
}
template <ResultCode func(u32*, u64, u64, s64)>
void SvcWrap() {
u32 param_1 = 0;
ResultCode retval = func(&param_1, Param(1), (u32)(Param(2) & 0xFFFFFFFF), (s64)Param(3));
ResultCode retval =
func(&param_1, Param(1), static_cast<u32>(Param(2)), static_cast<s64>(Param(3)));
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval.raw);
}
template <ResultCode func(u64, u64, u32, s64)>
void SvcWrap() {
FuncReturn(func(Param(0), Param(1), (u32)Param(2), (s64)Param(3)).raw);
FuncReturn(
func(Param(0), Param(1), static_cast<u32>(Param(2)), static_cast<s64>(Param(3))).raw);
}
template <ResultCode func(u64*, u64, u64, u64)>
@@ -147,9 +158,9 @@ void SvcWrap() {
template <ResultCode func(u32*, u64, u64, u64, u32, s32)>
void SvcWrap() {
u32 param_1 = 0;
u32 retval =
func(&param_1, Param(1), Param(2), Param(3), (u32)Param(4), (s32)(Param(5) & 0xFFFFFFFF))
.raw;
u32 retval = func(&param_1, Param(1), Param(2), Param(3), static_cast<u32>(Param(4)),
static_cast<s32>(Param(5)))
.raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
}
@@ -172,7 +183,7 @@ void SvcWrap() {
template <ResultCode func(u32*, u64, u64, u32)>
void SvcWrap() {
u32 param_1 = 0;
u32 retval = func(&param_1, Param(1), Param(2), (u32)(Param(3) & 0xFFFFFFFF)).raw;
u32 retval = func(&param_1, Param(1), Param(2), static_cast<u32>(Param(3))).raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
}
@@ -181,22 +192,22 @@ template <ResultCode func(Handle*, u64, u32, u32)>
void SvcWrap() {
u32 param_1 = 0;
u32 retval =
func(&param_1, Param(1), (u32)(Param(2) & 0xFFFFFFFF), (u32)(Param(3) & 0xFFFFFFFF)).raw;
func(&param_1, Param(1), static_cast<u32>(Param(2)), static_cast<u32>(Param(3))).raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
}
template <ResultCode func(u64, u32, s32, s64)>
void SvcWrap() {
FuncReturn(
func(Param(0), (u32)(Param(1) & 0xFFFFFFFF), (s32)(Param(2) & 0xFFFFFFFF), (s64)Param(3))
.raw);
FuncReturn(func(Param(0), static_cast<u32>(Param(1)), static_cast<s32>(Param(2)),
static_cast<s64>(Param(3)))
.raw);
}
template <ResultCode func(u64, u32, s32, s32)>
void SvcWrap() {
FuncReturn(func(Param(0), (u32)(Param(1) & 0xFFFFFFFF), (s32)(Param(2) & 0xFFFFFFFF),
(s32)(Param(3) & 0xFFFFFFFF))
FuncReturn(func(Param(0), static_cast<u32>(Param(1)), static_cast<s32>(Param(2)),
static_cast<s32>(Param(3)))
.raw);
}
@@ -226,7 +237,7 @@ void SvcWrap() {
template <void func(s64)>
void SvcWrap() {
func((s64)Param(0));
func(static_cast<s64>(Param(0)));
}
template <void func(u64, u64 len)>
@@ -239,4 +250,9 @@ void SvcWrap() {
func(Param(0), Param(1), Param(2));
}
template <void func(u32, u64, u64)>
void SvcWrap() {
func(static_cast<u32>(Param(0)), Param(1), Param(2));
}
} // namespace Kernel

View File

@@ -4,9 +4,9 @@
#include <algorithm>
#include <cinttypes>
#include <optional>
#include <vector>
#include <boost/optional.hpp>
#include <boost/range/algorithm_ext/erase.hpp>
#include "common/assert.h"
@@ -94,10 +94,10 @@ void Thread::CancelWakeupTimer() {
CoreTiming::UnscheduleEventThreadsafe(kernel.ThreadWakeupCallbackEventType(), callback_handle);
}
static boost::optional<s32> GetNextProcessorId(u64 mask) {
static std::optional<s32> GetNextProcessorId(u64 mask) {
for (s32 index = 0; index < Core::NUM_CPU_CORES; ++index) {
if (mask & (1ULL << index)) {
if (!Core::System::GetInstance().Scheduler(index)->GetCurrentThread()) {
if (!Core::System::GetInstance().Scheduler(index).GetCurrentThread()) {
// Core is enabled and not running any threads, use this one
return index;
}
@@ -142,19 +142,19 @@ void Thread::ResumeFromWait() {
status = ThreadStatus::Ready;
boost::optional<s32> new_processor_id = GetNextProcessorId(affinity_mask);
std::optional<s32> new_processor_id = GetNextProcessorId(affinity_mask);
if (!new_processor_id) {
new_processor_id = processor_id;
}
if (ideal_core != -1 &&
Core::System::GetInstance().Scheduler(ideal_core)->GetCurrentThread() == nullptr) {
Core::System::GetInstance().Scheduler(ideal_core).GetCurrentThread() == nullptr) {
new_processor_id = ideal_core;
}
ASSERT(*new_processor_id < 4);
// Add thread to new core's scheduler
auto& next_scheduler = Core::System::GetInstance().Scheduler(*new_processor_id);
auto* next_scheduler = &Core::System::GetInstance().Scheduler(*new_processor_id);
if (*new_processor_id != processor_id) {
// Remove thread from previous core's scheduler
@@ -169,7 +169,7 @@ void Thread::ResumeFromWait() {
next_scheduler->ScheduleThread(this, current_priority);
// Change thread's scheduler
scheduler = next_scheduler.get();
scheduler = next_scheduler;
Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
}
@@ -183,13 +183,10 @@ void Thread::ResumeFromWait() {
*/
static void ResetThreadContext(Core::ARM_Interface::ThreadContext& context, VAddr stack_top,
VAddr entry_point, u64 arg) {
memset(&context, 0, sizeof(Core::ARM_Interface::ThreadContext));
context = {};
context.cpu_registers[0] = arg;
context.pc = entry_point;
context.sp = stack_top;
context.pstate = 0;
context.fpcr = 0;
}
ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name, VAddr entry_point,
@@ -233,7 +230,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
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).get();
thread->scheduler = &Core::System::GetInstance().Scheduler(processor_id);
thread->scheduler->AddThread(thread, priority);
thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread);
@@ -269,7 +266,7 @@ SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 pri
SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
// Register 1 must be a handle to the main thread
const Handle guest_handle = kernel.HandleTable().Create(thread).Unwrap();
const Handle guest_handle = owner_process.GetHandleTable().Create(thread).Unwrap();
thread->SetGuestHandle(guest_handle);
thread->GetContext().cpu_registers[1] = guest_handle;
@@ -372,20 +369,20 @@ void Thread::ChangeCore(u32 core, u64 mask) {
return;
}
boost::optional<s32> new_processor_id{GetNextProcessorId(affinity_mask)};
std::optional<s32> new_processor_id{GetNextProcessorId(affinity_mask)};
if (!new_processor_id) {
new_processor_id = processor_id;
}
if (ideal_core != -1 &&
Core::System::GetInstance().Scheduler(ideal_core)->GetCurrentThread() == nullptr) {
Core::System::GetInstance().Scheduler(ideal_core).GetCurrentThread() == nullptr) {
new_processor_id = ideal_core;
}
ASSERT(*new_processor_id < 4);
// Add thread to new core's scheduler
auto& next_scheduler = Core::System::GetInstance().Scheduler(*new_processor_id);
auto* next_scheduler = &Core::System::GetInstance().Scheduler(*new_processor_id);
if (*new_processor_id != processor_id) {
// Remove thread from previous core's scheduler
@@ -400,7 +397,7 @@ void Thread::ChangeCore(u32 core, u64 mask) {
next_scheduler->ScheduleThread(this, current_priority);
// Change thread's scheduler
scheduler = next_scheduler.get();
scheduler = next_scheduler;
Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
}

View File

@@ -258,6 +258,14 @@ public:
return last_running_ticks;
}
u64 GetTotalCPUTimeTicks() const {
return total_cpu_time_ticks;
}
void UpdateCPUTimeTicks(u64 ticks) {
total_cpu_time_ticks += ticks;
}
s32 GetProcessorID() const {
return processor_id;
}
@@ -378,7 +386,8 @@ private:
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
u64 total_cpu_time_ticks = 0; ///< Total CPU running ticks.
u64 last_running_ticks = 0; ///< CPU tick when thread was last running
s32 processor_id = 0;

View File

@@ -143,6 +143,26 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* me
return MakeResult<VMAHandle>(MergeAdjacent(vma_handle));
}
ResultVal<VAddr> VMManager::FindFreeRegion(u64 size) const {
// Find the first Free VMA.
const VAddr base = GetASLRRegionBaseAddress();
const VMAHandle vma_handle = std::find_if(vma_map.begin(), vma_map.end(), [&](const auto& vma) {
if (vma.second.type != VMAType::Free)
return false;
const VAddr vma_end = vma.second.base + vma.second.size;
return vma_end > base && vma_end >= base + size;
});
if (vma_handle == vma_map.end()) {
// TODO(Subv): Find the correct error code here.
return ResultCode(-1);
}
const VAddr target = std::max(base, vma_handle->second.base);
return MakeResult<VAddr>(target);
}
ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u64 size,
MemoryState state,
Memory::MemoryHookPointer mmio_handler) {
@@ -393,30 +413,35 @@ void VMManager::InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType ty
switch (type) {
case FileSys::ProgramAddressSpaceType::Is32Bit:
case FileSys::ProgramAddressSpaceType::Is32BitNoMap:
address_space_width = 32;
code_region_base = 0x200000;
code_region_end = code_region_base + 0x3FE00000;
map_region_size = 0x40000000;
heap_region_size = 0x40000000;
aslr_region_base = 0x200000;
aslr_region_end = aslr_region_base + 0xFFE00000;
if (type == FileSys::ProgramAddressSpaceType::Is32Bit) {
map_region_size = 0x40000000;
heap_region_size = 0x40000000;
} else {
map_region_size = 0;
heap_region_size = 0x80000000;
}
break;
case FileSys::ProgramAddressSpaceType::Is36Bit:
address_space_width = 36;
code_region_base = 0x8000000;
code_region_end = code_region_base + 0x78000000;
aslr_region_base = 0x8000000;
aslr_region_end = aslr_region_base + 0xFF8000000;
map_region_size = 0x180000000;
heap_region_size = 0x180000000;
break;
case FileSys::ProgramAddressSpaceType::Is32BitNoMap:
address_space_width = 32;
code_region_base = 0x200000;
code_region_end = code_region_base + 0x3FE00000;
map_region_size = 0;
heap_region_size = 0x80000000;
break;
case FileSys::ProgramAddressSpaceType::Is39Bit:
address_space_width = 39;
code_region_base = 0x8000000;
code_region_end = code_region_base + 0x80000000;
aslr_region_base = 0x8000000;
aslr_region_end = aslr_region_base + 0x7FF8000000;
map_region_size = 0x1000000000;
heap_region_size = 0x180000000;
new_map_region_size = 0x80000000;
@@ -490,6 +515,38 @@ u64 VMManager::GetAddressSpaceWidth() const {
return address_space_width;
}
VAddr VMManager::GetASLRRegionBaseAddress() const {
return aslr_region_base;
}
VAddr VMManager::GetASLRRegionEndAddress() const {
return aslr_region_end;
}
u64 VMManager::GetASLRRegionSize() const {
return aslr_region_end - aslr_region_base;
}
bool VMManager::IsWithinASLRRegion(VAddr begin, u64 size) const {
const VAddr range_end = begin + size;
const VAddr aslr_start = GetASLRRegionBaseAddress();
const VAddr aslr_end = GetASLRRegionEndAddress();
if (aslr_start > begin || begin > range_end || range_end - 1 > aslr_end - 1) {
return false;
}
if (range_end > heap_region_base && heap_region_end > begin) {
return false;
}
if (range_end > map_region_base && map_region_end > begin) {
return false;
}
return true;
}
VAddr VMManager::GetCodeRegionBaseAddress() const {
return code_region_base;
}

View File

@@ -157,6 +157,14 @@ public:
*/
ResultVal<VMAHandle> MapBackingMemory(VAddr target, u8* memory, u64 size, MemoryState state);
/**
* Finds the first free address that can hold a region of the desired size.
*
* @param size Size of the desired region.
* @return The found free address.
*/
ResultVal<VAddr> FindFreeRegion(u64 size) const;
/**
* Maps a memory-mapped IO region at a given address.
*
@@ -205,6 +213,18 @@ public:
/// Gets the address space width in bits.
u64 GetAddressSpaceWidth() const;
/// Gets the base address of the ASLR region.
VAddr GetASLRRegionBaseAddress() const;
/// Gets the end address of the ASLR region.
VAddr GetASLRRegionEndAddress() const;
/// Determines whether or not the specified address range is within the ASLR region.
bool IsWithinASLRRegion(VAddr address, u64 size) const;
/// Gets the size of the ASLR region
u64 GetASLRRegionSize() const;
/// Gets the base address of the code region.
VAddr GetCodeRegionBaseAddress() const;
@@ -306,6 +326,9 @@ private:
VAddr address_space_base = 0;
VAddr address_space_end = 0;
VAddr aslr_region_base = 0;
VAddr aslr_region_end = 0;
VAddr code_region_base = 0;
VAddr code_region_end = 0;

View File

@@ -2,9 +2,13 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <array>
#include "common/common_paths.h"
#include "common/common_types.h"
#include "common/file_util.h"
#include "common/logging/log.h"
#include "common/string_util.h"
#include "common/swap.h"
#include "core/core_timing.h"
#include "core/hle/ipc_helpers.h"
@@ -16,6 +20,7 @@
#include "core/hle/service/acc/profile_manager.h"
namespace Service::Account {
// TODO: RE this structure
struct UserData {
INSERT_PADDING_WORDS(1);
@@ -27,6 +32,29 @@ struct UserData {
};
static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size");
// Smallest JPEG https://github.com/mathiasbynens/small/blob/master/jpeg.jpg
// used as a backup should the one on disk not exist
constexpr u32 backup_jpeg_size = 107;
constexpr std::array<u8, backup_jpeg_size> backup_jpeg{{
0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02,
0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05,
0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e,
0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13,
0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01,
0x01, 0x01, 0x11, 0x00, 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08,
0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9,
}};
static std::string GetImagePath(UUID uuid) {
return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
"/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg";
}
static constexpr u32 SanitizeJPEGSize(std::size_t size) {
constexpr std::size_t max_jpeg_image_size = 0x20000;
return static_cast<u32>(std::min(size, max_jpeg_image_size));
}
class IProfile final : public ServiceFramework<IProfile> {
public:
explicit IProfile(UUID user_id, ProfileManager& profile_manager)
@@ -73,32 +101,42 @@ private:
}
void LoadImage(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_ACC, "(STUBBED) called");
// smallest jpeg https://github.com/mathiasbynens/small/blob/master/jpeg.jpg
// TODO(mailwl): load actual profile image from disk, width 256px, max size 0x20000
constexpr u32 jpeg_size = 107;
static constexpr std::array<u8, jpeg_size> jpeg{
0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03,
0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04,
0x08, 0x06, 0x06, 0x05, 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a,
0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e, 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f,
0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13, 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10,
0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x11, 0x00,
0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08, 0x01, 0x01,
0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9,
};
ctx.WriteBuffer(jpeg);
LOG_DEBUG(Service_ACC, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(jpeg_size);
const FileUtil::IOFile image(GetImagePath(user_id), "rb");
if (!image.IsOpen()) {
LOG_WARNING(Service_ACC,
"Failed to load user provided image! Falling back to built-in backup...");
ctx.WriteBuffer(backup_jpeg);
rb.Push<u32>(backup_jpeg_size);
return;
}
const u32 size = SanitizeJPEGSize(image.GetSize());
std::vector<u8> buffer(size);
image.ReadBytes(buffer.data(), buffer.size());
ctx.WriteBuffer(buffer.data(), buffer.size());
rb.Push<u32>(size);
}
void GetImageSize(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_ACC, "(STUBBED) called");
constexpr u32 jpeg_size = 107;
LOG_DEBUG(Service_ACC, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(jpeg_size);
const FileUtil::IOFile image(GetImagePath(user_id), "rb");
if (!image.IsOpen()) {
LOG_WARNING(Service_ACC,
"Failed to load user provided image! Falling back to built-in backup...");
rb.Push<u32>(backup_jpeg_size);
} else {
rb.Push<u32>(SanitizeJPEGSize(image.GetSize()));
}
}
const ProfileManager& profile_manager;
@@ -204,6 +242,28 @@ void Module::Interface::GetBaasAccountManagerForApplication(Kernel::HLERequestCo
LOG_DEBUG(Service_ACC, "called");
}
void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_ACC, "called");
// A u8 is passed into this function which we can safely ignore. It's to determine if we have
// access to use the network or not by the looks of it
IPC::ResponseBuilder rb{ctx, 6};
if (profile_manager->GetUserCount() != 1) {
rb.Push(RESULT_SUCCESS);
rb.PushRaw<u128>(INVALID_UUID);
return;
}
auto user_list = profile_manager->GetAllUsers();
if (user_list.empty()) {
rb.Push(ResultCode(-1)); // TODO(ogniK): Find the correct error code
rb.PushRaw<u128>(INVALID_UUID);
return;
}
// Select the first user we have
rb.Push(RESULT_SUCCESS);
rb.PushRaw<u128>(profile_manager->GetUser(0)->uuid);
}
Module::Interface::Interface(std::shared_ptr<Module> module,
std::shared_ptr<ProfileManager> profile_manager, const char* name)
: ServiceFramework(name), module(std::move(module)),

View File

@@ -27,6 +27,7 @@ public:
void InitializeApplicationInfo(Kernel::HLERequestContext& ctx);
void GetBaasAccountManagerForApplication(Kernel::HLERequestContext& ctx);
void IsUserRegistrationRequestPermitted(Kernel::HLERequestContext& ctx);
void TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx);
protected:
std::shared_ptr<Module> module;

View File

@@ -17,7 +17,7 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
{5, &ACC_SU::GetProfile, "GetProfile"},
{6, nullptr, "GetProfileDigest"},
{50, &ACC_SU::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
{51, nullptr, "TrySelectUserWithoutInteraction"},
{51, &ACC_SU::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
{60, nullptr, "ListOpenContextStoredUsers"},
{100, nullptr, "GetUserRegistrationNotifier"},
{101, nullptr, "GetUserStateChangeNotifier"},

View File

@@ -17,7 +17,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
{5, &ACC_U0::GetProfile, "GetProfile"},
{6, nullptr, "GetProfileDigest"},
{50, &ACC_U0::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
{51, nullptr, "TrySelectUserWithoutInteraction"},
{51, &ACC_U0::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
{60, nullptr, "ListOpenContextStoredUsers"},
{100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"},
{101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"},

View File

@@ -17,7 +17,7 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
{5, &ACC_U1::GetProfile, "GetProfile"},
{6, nullptr, "GetProfileDigest"},
{50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
{51, nullptr, "TrySelectUserWithoutInteraction"},
{51, &ACC_U1::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
{60, nullptr, "ListOpenContextStoredUsers"},
{100, nullptr, "GetUserRegistrationNotifier"},
{101, nullptr, "GetUserStateChangeNotifier"},

View File

@@ -3,41 +3,66 @@
// Refer to the license.txt file included.
#include <random>
#include <boost/optional.hpp>
#include "common/file_util.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/settings.h"
namespace Service::Account {
struct UserRaw {
UUID uuid;
UUID uuid2;
u64 timestamp;
ProfileUsername username;
INSERT_PADDING_BYTES(0x80);
};
static_assert(sizeof(UserRaw) == 0xC8, "UserRaw has incorrect size.");
struct ProfileDataRaw {
INSERT_PADDING_BYTES(0x10);
std::array<UserRaw, MAX_USERS> users;
};
static_assert(sizeof(ProfileDataRaw) == 0x650, "ProfileDataRaw has incorrect size.");
// TODO(ogniK): Get actual error codes
constexpr ResultCode ERROR_TOO_MANY_USERS(ErrorModule::Account, -1);
constexpr ResultCode ERROR_USER_ALREADY_EXISTS(ErrorModule::Account, -2);
constexpr ResultCode ERROR_ARGUMENT_IS_NULL(ErrorModule::Account, 20);
const UUID& UUID::Generate() {
constexpr char ACC_SAVE_AVATORS_BASE_PATH[] = "/system/save/8000000000000010/su/avators/";
UUID UUID::Generate() {
std::random_device device;
std::mt19937 gen(device());
std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
uuid[0] = distribution(gen);
uuid[1] = distribution(gen);
return *this;
return UUID{distribution(gen), distribution(gen)};
}
ProfileManager::ProfileManager() {
// TODO(ogniK): Create the default user we have for now until loading/saving users is added
auto user_uuid = UUID{1, 0};
ASSERT(CreateNewUser(user_uuid, Settings::values.username).IsSuccess());
OpenUser(user_uuid);
ParseUserSaveFile();
if (user_count == 0)
CreateNewUser(UUID::Generate(), "yuzu");
auto current = std::clamp<int>(Settings::values.current_user, 0, MAX_USERS - 1);
if (UserExistsIndex(current))
current = 0;
OpenUser(*GetUser(current));
}
ProfileManager::~ProfileManager() = default;
ProfileManager::~ProfileManager() {
WriteUserSaveFile();
}
/// After a users creation it needs to be "registered" to the system. AddToProfiles handles the
/// internal management of the users profiles
boost::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& user) {
std::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& profile) {
if (user_count >= MAX_USERS) {
return boost::none;
return {};
}
profiles[user_count] = user;
profiles[user_count] = profile;
return user_count++;
}
@@ -56,7 +81,7 @@ bool ProfileManager::RemoveProfileAtIndex(std::size_t index) {
/// Helper function to register a user to the system
ResultCode ProfileManager::AddUser(const ProfileInfo& user) {
if (AddToProfiles(user) == boost::none) {
if (!AddToProfiles(user)) {
return ERROR_TOO_MANY_USERS;
}
return RESULT_SUCCESS;
@@ -101,31 +126,40 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const std::string& username)
return CreateNewUser(uuid, username_output);
}
std::optional<UUID> ProfileManager::GetUser(std::size_t index) const {
if (index >= MAX_USERS) {
return {};
}
return profiles[index].user_uuid;
}
/// Returns a users profile index based on their user id.
boost::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const {
std::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const {
if (!uuid) {
return boost::none;
return {};
}
auto iter = std::find_if(profiles.begin(), profiles.end(),
[&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; });
const auto iter = std::find_if(profiles.begin(), profiles.end(),
[&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; });
if (iter == profiles.end()) {
return boost::none;
return {};
}
return static_cast<std::size_t>(std::distance(profiles.begin(), iter));
}
/// Returns a users profile index based on their profile
boost::optional<std::size_t> ProfileManager::GetUserIndex(const ProfileInfo& user) const {
std::optional<std::size_t> ProfileManager::GetUserIndex(const ProfileInfo& user) const {
return GetUserIndex(user.user_uuid);
}
/// Returns the data structure used by the switch when GetProfileBase is called on acc:*
bool ProfileManager::GetProfileBase(boost::optional<std::size_t> index,
ProfileBase& profile) const {
if (index == boost::none || index >= MAX_USERS) {
bool ProfileManager::GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const {
if (!index || index >= MAX_USERS) {
return false;
}
const auto& prof_info = profiles[index.get()];
const auto& prof_info = profiles[*index];
profile.user_uuid = prof_info.user_uuid;
profile.username = prof_info.username;
profile.timestamp = prof_info.creation_time;
@@ -134,7 +168,7 @@ bool ProfileManager::GetProfileBase(boost::optional<std::size_t> index,
/// Returns the data structure used by the switch when GetProfileBase is called on acc:*
bool ProfileManager::GetProfileBase(UUID uuid, ProfileBase& profile) const {
auto idx = GetUserIndex(uuid);
const auto idx = GetUserIndex(uuid);
return GetProfileBase(idx, profile);
}
@@ -161,26 +195,34 @@ std::size_t ProfileManager::GetOpenUserCount() const {
/// Checks if a user id exists in our profile manager
bool ProfileManager::UserExists(UUID uuid) const {
return (GetUserIndex(uuid) != boost::none);
return GetUserIndex(uuid).has_value();
}
bool ProfileManager::UserExistsIndex(std::size_t index) const {
if (index >= MAX_USERS)
return false;
return profiles[index].user_uuid.uuid != INVALID_UUID;
}
/// Opens a specific user
void ProfileManager::OpenUser(UUID uuid) {
auto idx = GetUserIndex(uuid);
if (idx == boost::none) {
const auto idx = GetUserIndex(uuid);
if (!idx) {
return;
}
profiles[idx.get()].is_open = true;
profiles[*idx].is_open = true;
last_opened_user = uuid;
}
/// Closes a specific user
void ProfileManager::CloseUser(UUID uuid) {
auto idx = GetUserIndex(uuid);
if (idx == boost::none) {
const auto idx = GetUserIndex(uuid);
if (!idx) {
return;
}
profiles[idx.get()].is_open = false;
profiles[*idx].is_open = false;
}
/// Gets all valid user ids on the system
@@ -210,10 +252,10 @@ UUID ProfileManager::GetLastOpenedUser() const {
}
/// Return the users profile base and the unknown arbitary data.
bool ProfileManager::GetProfileBaseAndData(boost::optional<std::size_t> index, ProfileBase& profile,
bool ProfileManager::GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile,
ProfileData& data) const {
if (GetProfileBase(index, profile)) {
data = profiles[index.get()].data;
data = profiles[*index].data;
return true;
}
return false;
@@ -222,7 +264,7 @@ bool ProfileManager::GetProfileBaseAndData(boost::optional<std::size_t> index, P
/// Return the users profile base and the unknown arbitary data.
bool ProfileManager::GetProfileBaseAndData(UUID uuid, ProfileBase& profile,
ProfileData& data) const {
auto idx = GetUserIndex(uuid);
const auto idx = GetUserIndex(uuid);
return GetProfileBaseAndData(idx, profile, data);
}
@@ -239,4 +281,96 @@ bool ProfileManager::CanSystemRegisterUser() const {
// emulate qlaunch. Update this to dynamically change.
}
bool ProfileManager::RemoveUser(UUID uuid) {
const auto index = GetUserIndex(uuid);
if (!index) {
return false;
}
profiles[*index] = ProfileInfo{};
std::stable_partition(profiles.begin(), profiles.end(),
[](const ProfileInfo& profile) { return profile.user_uuid; });
return true;
}
bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) {
const auto index = GetUserIndex(uuid);
if (!index || profile_new.user_uuid == UUID(INVALID_UUID)) {
return false;
}
auto& profile = profiles[*index];
profile.user_uuid = profile_new.user_uuid;
profile.username = profile_new.username;
profile.creation_time = profile_new.timestamp;
return true;
}
void ProfileManager::ParseUserSaveFile() {
FileUtil::IOFile save(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat",
"rb");
if (!save.IsOpen()) {
LOG_WARNING(Service_ACC, "Failed to load profile data from save data... Generating new "
"user 'yuzu' with random UUID.");
return;
}
ProfileDataRaw data;
if (save.ReadBytes(&data, sizeof(ProfileDataRaw)) != sizeof(ProfileDataRaw)) {
LOG_WARNING(Service_ACC, "profiles.dat is smaller than expected... Generating new user "
"'yuzu' with random UUID.");
return;
}
for (std::size_t i = 0; i < MAX_USERS; ++i) {
const auto& user = data.users[i];
if (user.uuid != UUID(INVALID_UUID))
AddUser({user.uuid, user.username, user.timestamp, {}, false});
}
std::stable_partition(profiles.begin(), profiles.end(),
[](const ProfileInfo& profile) { return profile.user_uuid; });
}
void ProfileManager::WriteUserSaveFile() {
ProfileDataRaw raw{};
for (std::size_t i = 0; i < MAX_USERS; ++i) {
raw.users[i].username = profiles[i].username;
raw.users[i].uuid2 = profiles[i].user_uuid;
raw.users[i].uuid = profiles[i].user_uuid;
raw.users[i].timestamp = profiles[i].creation_time;
}
const auto raw_path =
FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010";
if (FileUtil::Exists(raw_path) && !FileUtil::IsDirectory(raw_path))
FileUtil::Delete(raw_path);
const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat";
if (!FileUtil::CreateFullPath(path)) {
LOG_WARNING(Service_ACC, "Failed to create full path of profiles.dat. Create the directory "
"nand/system/save/8000000000000010/su/avators to mitigate this "
"issue.");
return;
}
FileUtil::IOFile save(path, "wb");
if (!save.IsOpen()) {
LOG_WARNING(Service_ACC, "Failed to write save data to file... No changes to user data "
"made in current session will be saved.");
return;
}
save.Resize(sizeof(ProfileDataRaw));
save.WriteBytes(&raw, sizeof(ProfileDataRaw));
}
}; // namespace Service::Account

View File

@@ -5,8 +5,8 @@
#pragma once
#include <array>
#include <optional>
#include "boost/optional.hpp"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/result.h"
@@ -36,7 +36,7 @@ struct UUID {
}
// TODO(ogniK): Properly generate uuids based on RFC-4122
const UUID& Generate();
static UUID Generate();
// Set the UUID to {0,0} to be considered an invalid user
void Invalidate() {
@@ -45,10 +45,20 @@ struct UUID {
std::string Format() const {
return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]);
}
std::string FormatSwitch() const {
std::array<u8, 16> s{};
std::memcpy(s.data(), uuid.data(), sizeof(u128));
return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{"
":02x}{:02x}{:02x}{:02x}{:02x}",
s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11],
s[12], s[13], s[14], s[15]);
}
};
static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
using ProfileUsername = std::array<u8, 0x20>;
constexpr std::size_t profile_username_size = 32;
using ProfileUsername = std::array<u8, profile_username_size>;
using ProfileData = std::array<u8, MAX_DATA>;
using UserIDArray = std::array<UUID, MAX_USERS>;
@@ -81,18 +91,19 @@ static_assert(sizeof(ProfileBase) == 0x38, "ProfileBase is an invalid size");
/// objects
class ProfileManager {
public:
ProfileManager(); // TODO(ogniK): Load from system save
ProfileManager();
~ProfileManager();
ResultCode AddUser(const ProfileInfo& user);
ResultCode CreateNewUser(UUID uuid, const ProfileUsername& username);
ResultCode CreateNewUser(UUID uuid, const std::string& username);
boost::optional<std::size_t> GetUserIndex(const UUID& uuid) const;
boost::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const;
bool GetProfileBase(boost::optional<std::size_t> index, ProfileBase& profile) const;
std::optional<UUID> GetUser(std::size_t index) const;
std::optional<std::size_t> GetUserIndex(const UUID& uuid) const;
std::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const;
bool GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const;
bool GetProfileBase(UUID uuid, ProfileBase& profile) const;
bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const;
bool GetProfileBaseAndData(boost::optional<std::size_t> index, ProfileBase& profile,
bool GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile,
ProfileData& data) const;
bool GetProfileBaseAndData(UUID uuid, ProfileBase& profile, ProfileData& data) const;
bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile,
@@ -100,6 +111,7 @@ public:
std::size_t GetUserCount() const;
std::size_t GetOpenUserCount() const;
bool UserExists(UUID uuid) const;
bool UserExistsIndex(std::size_t index) const;
void OpenUser(UUID uuid);
void CloseUser(UUID uuid);
UserIDArray GetOpenUsers() const;
@@ -108,11 +120,17 @@ public:
bool CanSystemRegisterUser() const;
bool RemoveUser(UUID uuid);
bool SetProfileBase(UUID uuid, const ProfileBase& profile_new);
private:
void ParseUserSaveFile();
void WriteUserSaveFile();
std::optional<std::size_t> AddToProfiles(const ProfileInfo& profile);
bool RemoveProfileAtIndex(std::size_t index);
std::array<ProfileInfo, MAX_USERS> profiles{};
std::size_t user_count = 0;
boost::optional<std::size_t> AddToProfiles(const ProfileInfo& profile);
bool RemoveProfileAtIndex(std::size_t index);
UUID last_opened_user{INVALID_UUID};
};

View File

@@ -4,17 +4,20 @@
#include <array>
#include <cinttypes>
#include <cstring>
#include <stack>
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/am/applet_oe.h"
#include "core/hle/service/am/idle.h"
#include "core/hle/service/am/omm.h"
#include "core/hle/service/am/spsm.h"
#include "core/hle/service/am/tcap.h"
#include "core/hle/service/apm/apm.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/nvflinger/nvflinger.h"
@@ -25,14 +28,29 @@
namespace Service::AM {
constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA;
struct LaunchParameters {
u32_le magic;
u32_le is_account_selected;
u128 current_user;
INSERT_PADDING_BYTES(0x70);
};
static_assert(sizeof(LaunchParameters) == 0x88);
IWindowController::IWindowController() : ServiceFramework("IWindowController") {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "CreateWindow"},
{1, &IWindowController::GetAppletResourceUserId, "GetAppletResourceUserId"},
{10, &IWindowController::AcquireForegroundRights, "AcquireForegroundRights"},
{11, nullptr, "ReleaseForegroundRights"},
{12, nullptr, "RejectToChangeIntoBackground"},
{20, nullptr, "SetAppletWindowVisibility"},
{21, nullptr, "SetAppletGpuTimeSlice"},
};
// clang-format on
RegisterHandlers(functions);
}
@@ -87,6 +105,7 @@ void IAudioController::GetLibraryAppletExpectedMasterVolume(Kernel::HLERequestCo
}
IDisplayController::IDisplayController() : ServiceFramework("IDisplayController") {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetLastForegroundCaptureImage"},
{1, nullptr, "UpdateLastForegroundCaptureImage"},
@@ -117,7 +136,11 @@ IDisplayController::IDisplayController() : ServiceFramework("IDisplayController"
{25, nullptr, "ReleaseLastForegroundCaptureSharedBuffer"},
{26, nullptr, "AcquireCallerAppletCaptureSharedBuffer"},
{27, nullptr, "ReleaseCallerAppletCaptureSharedBuffer"},
// 6.0.0+
{28, nullptr, "TakeScreenShotOfOwnLayerEx"},
};
// clang-format on
RegisterHandlers(functions);
}
@@ -128,6 +151,7 @@ IDebugFunctions::~IDebugFunctions() = default;
ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
: ServiceFramework("ISelfController"), nvflinger(std::move(nvflinger)) {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "Exit"},
{1, &ISelfController::LockExit, "LockExit"},
@@ -136,10 +160,8 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
{4, nullptr, "LeaveFatalSection"},
{9, &ISelfController::GetLibraryAppletLaunchableEvent, "GetLibraryAppletLaunchableEvent"},
{10, &ISelfController::SetScreenShotPermission, "SetScreenShotPermission"},
{11, &ISelfController::SetOperationModeChangedNotification,
"SetOperationModeChangedNotification"},
{12, &ISelfController::SetPerformanceModeChangedNotification,
"SetPerformanceModeChangedNotification"},
{11, &ISelfController::SetOperationModeChangedNotification, "SetOperationModeChangedNotification"},
{12, &ISelfController::SetPerformanceModeChangedNotification, "SetPerformanceModeChangedNotification"},
{13, &ISelfController::SetFocusHandlingMode, "SetFocusHandlingMode"},
{14, &ISelfController::SetRestartMessageEnabled, "SetRestartMessageEnabled"},
{15, nullptr, "SetScreenShotAppletIdentityInfo"},
@@ -165,7 +187,12 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
{69, nullptr, "IsAutoSleepDisabled"},
{70, nullptr, "ReportMultimediaError"},
{80, nullptr, "SetWirelessPriorityMode"},
{90, nullptr, "GetAccumulatedSuspendedTickValue"},
{91, nullptr, "GetAccumulatedSuspendedTickChangedEvent"},
{1000, nullptr, "GetDebugStorageChannel"},
};
// clang-format on
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
@@ -312,6 +339,7 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c
}
ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter") {
// clang-format off
static const FunctionInfo functions[] = {
{0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"},
{1, &ICommonStateGetter::ReceiveMessage, "ReceiveMessage"},
@@ -336,11 +364,12 @@ ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter"
{52, nullptr, "SwitchLcdBacklight"},
{55, nullptr, "IsInControllerFirmwareUpdateSection"},
{60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"},
{61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent,
"GetDefaultDisplayResolutionChangeEvent"},
{61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, "GetDefaultDisplayResolutionChangeEvent"},
{62, nullptr, "GetHdcpAuthenticationState"},
{63, nullptr, "GetHdcpAuthenticationStateChangeEvent"},
};
// clang-format on
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
@@ -432,11 +461,14 @@ class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
public:
explicit IStorageAccessor(std::vector<u8> buffer)
: ServiceFramework("IStorageAccessor"), buffer(std::move(buffer)) {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IStorageAccessor::GetSize, "GetSize"},
{10, &IStorageAccessor::Write, "Write"},
{11, &IStorageAccessor::Read, "Read"},
};
// clang-format on
RegisterHandlers(functions);
}
@@ -489,10 +521,13 @@ class IStorage final : public ServiceFramework<IStorage> {
public:
explicit IStorage(std::vector<u8> buffer)
: ServiceFramework("IStorage"), buffer(std::move(buffer)) {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IStorage::Open, "Open"},
{1, nullptr, "OpenTransferStorage"},
};
// clang-format on
RegisterHandlers(functions);
}
@@ -512,6 +547,7 @@ private:
class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> {
public:
explicit ILibraryAppletAccessor() : ServiceFramework("ILibraryAppletAccessor") {
// clang-format off
static const FunctionInfo functions[] = {
{0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"},
{1, nullptr, "IsCompleted"},
@@ -532,6 +568,8 @@ public:
{150, nullptr, "RequestForAppletToGetForeground"},
{160, nullptr, "GetIndirectLayerConsumerHandle"},
};
// clang-format on
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
@@ -624,13 +662,13 @@ void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) {
}
IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") {
// clang-format off
static const FunctionInfo functions[] = {
{1, &IApplicationFunctions::PopLaunchParameter, "PopLaunchParameter"},
{10, nullptr, "CreateApplicationAndPushAndRequestToStart"},
{11, nullptr, "CreateApplicationAndPushAndRequestToStartForQuest"},
{12, nullptr, "CreateApplicationAndRequestToStart"},
{13, &IApplicationFunctions::CreateApplicationAndRequestToStartForQuest,
"CreateApplicationAndRequestToStartForQuest"},
{13, &IApplicationFunctions::CreateApplicationAndRequestToStartForQuest, "CreateApplicationAndRequestToStartForQuest"},
{20, &IApplicationFunctions::EnsureSaveData, "EnsureSaveData"},
{21, &IApplicationFunctions::GetDesiredLanguage, "GetDesiredLanguage"},
{22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"},
@@ -638,10 +676,10 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
{24, nullptr, "GetLaunchStorageInfoForDebug"},
{25, nullptr, "ExtendSaveData"},
{26, nullptr, "GetSaveDataSize"},
{30, nullptr, "BeginBlockingHomeButtonShortAndLongPressed"},
{31, nullptr, "EndBlockingHomeButtonShortAndLongPressed"},
{32, nullptr, "BeginBlockingHomeButton"},
{33, nullptr, "EndBlockingHomeButton"},
{30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, "BeginBlockingHomeButtonShortAndLongPressed"},
{31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"},
{32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"},
{33, &IApplicationFunctions::EndBlockingHomeButton, "EndBlockingHomeButton"},
{40, &IApplicationFunctions::NotifyRunning, "NotifyRunning"},
{50, &IApplicationFunctions::GetPseudoDeviceId, "GetPseudoDeviceId"},
{60, nullptr, "SetMediaPlaybackStateForApplication"},
@@ -664,26 +702,57 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
{1000, nullptr, "CreateMovieMaker"},
{1001, nullptr, "PrepareForJit"},
};
// clang-format on
RegisterHandlers(functions);
}
IApplicationFunctions::~IApplicationFunctions() = default;
void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
constexpr std::array<u8, 0x88> data{{
0xca, 0x97, 0x94, 0xc7, // Magic
1, 0, 0, 0, // IsAccountSelected (bool)
1, 0, 0, 0, // User Id (word 0)
0, 0, 0, 0, // User Id (word 1)
0, 0, 0, 0, // User Id (word 2)
0, 0, 0, 0 // User Id (word 3)
}};
void IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed(
Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_AM, "(STUBBED) called");
}
std::vector<u8> buffer(data.begin(), data.end());
void IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed(
Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_AM, "(STUBBED) called");
}
void IApplicationFunctions::BeginBlockingHomeButton(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_AM, "(STUBBED) called");
}
void IApplicationFunctions::EndBlockingHomeButton(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
LOG_WARNING(Service_AM, "(STUBBED) called");
}
void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
LaunchParameters params{};
params.magic = POP_LAUNCH_PARAMETER_MAGIC;
params.is_account_selected = 1;
Account::ProfileManager profile_manager{};
const auto uuid = profile_manager.GetUser(Settings::values.current_user);
ASSERT(uuid);
params.current_user = uuid->uuid;
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
std::vector<u8> buffer(sizeof(LaunchParameters));
std::memcpy(buffer.data(), &params, buffer.size());
rb.PushIpcInterface<AM::IStorage>(buffer);
LOG_DEBUG(Service_AM, "called");
@@ -776,9 +845,11 @@ void InstallInterfaces(SM::ServiceManager& service_manager,
std::make_shared<IdleSys>()->InstallAsService(service_manager);
std::make_shared<OMM>()->InstallAsService(service_manager);
std::make_shared<SPSM>()->InstallAsService(service_manager);
std::make_shared<TCAP>()->InstallAsService(service_manager);
}
IHomeMenuFunctions::IHomeMenuFunctions() : ServiceFramework("IHomeMenuFunctions") {
// clang-format off
static const FunctionInfo functions[] = {
{10, &IHomeMenuFunctions::RequestToGetForeground, "RequestToGetForeground"},
{11, nullptr, "LockForeground"},
@@ -787,7 +858,10 @@ IHomeMenuFunctions::IHomeMenuFunctions() : ServiceFramework("IHomeMenuFunctions"
{21, nullptr, "GetPopFromGeneralChannelEvent"},
{30, nullptr, "GetHomeButtonWriterLockAccessor"},
{31, nullptr, "GetWriterLockAccessorEx"},
{100, nullptr, "PopRequestLaunchApplicationForDebug"},
};
// clang-format on
RegisterHandlers(functions);
}
@@ -800,6 +874,7 @@ void IHomeMenuFunctions::RequestToGetForeground(Kernel::HLERequestContext& ctx)
}
IGlobalStateController::IGlobalStateController() : ServiceFramework("IGlobalStateController") {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "RequestToEnterSleep"},
{1, nullptr, "EnterSleep"},
@@ -813,18 +888,23 @@ IGlobalStateController::IGlobalStateController() : ServiceFramework("IGlobalStat
{14, nullptr, "ShouldSleepOnBoot"},
{15, nullptr, "GetHdcpAuthenticationFailedEvent"},
};
// clang-format on
RegisterHandlers(functions);
}
IGlobalStateController::~IGlobalStateController() = default;
IApplicationCreator::IApplicationCreator() : ServiceFramework("IApplicationCreator") {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "CreateApplication"},
{1, nullptr, "PopLaunchRequestedApplication"},
{10, nullptr, "CreateSystemApplication"},
{100, nullptr, "PopFloatingApplicationForDevelopment"},
};
// clang-format on
RegisterHandlers(functions);
}
@@ -832,6 +912,7 @@ IApplicationCreator::~IApplicationCreator() = default;
IProcessWindingController::IProcessWindingController()
: ServiceFramework("IProcessWindingController") {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetLaunchReason"},
{11, nullptr, "OpenCallingLibraryApplet"},
@@ -842,6 +923,8 @@ IProcessWindingController::IProcessWindingController()
{40, nullptr, "ReserveToStartAndWaitAndUnwindThis"},
{41, nullptr, "ReserveToStartAndWait"},
};
// clang-format on
RegisterHandlers(functions);
}

View File

@@ -154,6 +154,10 @@ private:
void SetGamePlayRecordingState(Kernel::HLERequestContext& ctx);
void NotifyRunning(Kernel::HLERequestContext& ctx);
void GetPseudoDeviceId(Kernel::HLERequestContext& ctx);
void BeginBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx);
void EndBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx);
void BeginBlockingHomeButton(Kernel::HLERequestContext& ctx);
void EndBlockingHomeButton(Kernel::HLERequestContext& ctx);
};
class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> {

View File

@@ -211,6 +211,7 @@ void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) {
AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
: ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)) {
// clang-format off
static const FunctionInfo functions[] = {
{100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"},
{200, &AppletAE::OpenLibraryAppletProxyOld, "OpenLibraryAppletProxyOld"},
@@ -218,7 +219,10 @@ AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
{300, nullptr, "OpenOverlayAppletProxy"},
{350, nullptr, "OpenSystemApplicationProxy"},
{400, nullptr, "CreateSelfLibraryAppletCreatorForDevelop"},
{401, nullptr, "GetSystemAppletControllerForDebug"},
};
// clang-format on
RegisterHandlers(functions);
}

View File

@@ -14,6 +14,7 @@ class IApplicationProxy final : public ServiceFramework<IApplicationProxy> {
public:
explicit IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
: ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)) {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"},
{1, &IApplicationProxy::GetSelfController, "GetSelfController"},
@@ -25,6 +26,8 @@ public:
{20, &IApplicationProxy::GetApplicationFunctions, "GetApplicationFunctions"},
{1000, &IApplicationProxy::GetDebugFunctions, "GetDebugFunctions"},
};
// clang-format on
RegisterHandlers(functions);
}

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