Compare commits

...
Author SHA1 Message Date
yuzubot 50bdd2c682 "Merge Tagged PR 1012" 2019-10-08 12:01:26 +00:00
yuzubot f13bae45d0 "Merge Tagged PR 1340" 2019-10-08 12:01:25 +00:00
yuzubot f709f3fbb7 "Merge Tagged PR 1703" 2019-10-08 12:01:24 +00:00
yuzubot 9290bd9d22 "Merge Tagged PR 2365" 2019-10-08 12:01:23 +00:00
yuzubot 4837674bf4 "Merge Tagged PR 2542" 2019-10-08 12:01:22 +00:00
yuzubot f3dd2b733c "Merge Tagged PR 2710" 2019-10-08 12:01:21 +00:00
yuzubot 7a57e6ce3c "Merge Tagged PR 2859" 2019-10-08 12:01:21 +00:00
yuzubot aafc842082 "Merge Tagged PR 2860" 2019-10-08 12:01:20 +00:00
yuzubot 06ba9a7f86 "Merge Tagged PR 2912" 2019-10-08 12:01:19 +00:00
yuzubot c84b917e12 "Merge Tagged PR 2914" 2019-10-08 12:01:18 +00:00
yuzubot fa8316e5da "Merge Tagged PR 2927" 2019-10-08 12:01:17 +00:00
yuzubot 61406e3088 "Merge Tagged PR 2945" 2019-10-08 12:01:16 +00:00
yuzubot a66a70f0cd "Merge Tagged PR 2959" 2019-10-08 12:01:16 +00:00
bunnei e53e6b43f5 Merge pull request #2890 from Morph1984/hid_deactivatenpad
hid: Implement DeactivateNpad
2019-10-07 12:26:32 -04:00
Morph 29e15601f3 hid: Implement DeactivateNpad
Makes use of the already existing DeactivateController function.
2019-10-07 04:44:09 -07:00
Zach Hilman 5326d3cb3a Merge pull request #2951 from lioncash/global
core: Remove Core::CurrentProcess()
2019-10-06 20:47:48 -04:00
Rodrigo Locatti dcdd887df3 Merge pull request #2952 from lioncash/warning
bcat: Silence various warnings
2019-10-06 23:35:12 +00:00
Rodrigo Locatti 3cb4c9f08a Merge pull request #2955 from lioncash/allocator
alignment: Resolve allocator construction issues on debug
2019-10-06 23:30:54 +00:00
Lioncash 57ffada746 alignment: Resolve allocator construction issues on debug
This was related to the source allocator being passed into the
constructor potentially having a different type than allocator being
constructed.

We simply need to provide a constructor to handle this case.

This resolves issues related to the allocator causing debug builds on
MSVC to fail.
2019-10-06 18:49:49 -04:00
Lioncash 9aafb2a277 alignment: Specify trait definitions within the allocator
Allows containers and other data structures to consider optimizations
based off of them. We satisfy all of these requirements anyways.
2019-10-06 18:48:05 -04:00
Fernando Sahmkow 8f407adeeb Merge pull request #2954 from ReinUsesLisp/fix-invalidation
gl_shader_disk_cache: Properly ignore existing cache
2019-10-06 17:48:14 -04:00
ReinUsesLisp 58b597c5ec gl_shader_disk_cache: Properly ignore existing cache
Previously old entries where appended to the file even if the shader
cache was ignored at boot. Address that issue.
2019-10-06 18:00:20 -03:00
Lioncash 0705ce0ed1 bcat/module: Silence truncation warnings
We need to perform explicit casts here, otherwise we're implicitly
truncating a 64-bit type to a 32-bit one.
2019-10-06 15:38:12 -04:00
Lioncash 7e77d1593f bcat: Take std::function instance by value in NullBackend's constructor
Without this, the std::move within the constructor initializer list
won't be able to actually perform a move.
2019-10-06 15:38:12 -04:00
Lioncash 81adf46d1d bcat: In-class initialize ProgressServiceBackend's impl member
Allows us to remove a constructor initializer list.
2019-10-06 15:38:12 -04:00
Lioncash 3fcd2180e4 bcat: Make ProgressServiceBackend's constructor take a std::string_view
Given the string is appended to another, we can make it a view so a
redundant full copy of the string isn't made.
2019-10-06 15:38:09 -04:00
bunnei 123c0580b4 Merge pull request #2953 from DarkLordZach/azure-titlebars-gamename
qt: Fix game name format error
2019-10-06 15:13:57 -04:00
Zach Hilman 81fff7aec0 qt: Fix game name format error 2019-10-06 15:07:04 -04:00
Lioncash f2fa16b609 bcat: Make ProgressServiceBackend's GetEvent() const
This member function doesn't modify internal member state, so it can be
marked const.
2019-10-06 14:03:18 -04:00
Lioncash 329081fcb7 boxcat: Silence an unused variable warning
On parse errors, we can log out the explanatory string indicating what
the parsing error was, rather than just ignoring the variable and
returning an overly broad error code.
2019-10-06 14:00:32 -04:00
Lioncash 839b38c404 core/core: Remove unused header
This isn't used anywhere in either the cpp or header file.
2019-10-06 13:54:27 -04:00
Lioncash f1382cf0e7 core: Remove Core::CurrentProcess()
This only encourages the use of the global system instance (which will
be phased out long-term). Instead, we use the direct system function
call directly to remove the appealing but discouraged short-hand.
2019-10-06 13:53:12 -04:00
Lioncash 69f16ba50e hle/service: Replace global system instance calls with instance-based ones
Migrates the HLE service code off the use of directly accessing the
global system instance where trivially able to do so.

This removes all usages of Core::CurrentProcess from the service code,
only 8 occurrences of this function exist elsewhere. There's still quite
a bit of "System::GetInstance()" being used, however this was able to
replace a few instances.
2019-10-06 13:42:23 -04:00
bunnei deecd7f074 Merge pull request #2942 from ReinUsesLisp/clang-warnings
Silence miscellaneous warnings
2019-10-05 20:41:20 -04:00
bunnei 6f511c8006 Merge pull request #2943 from DarkLordZach/azure-titlebars-v2
ci: Add custom titlebars for mainline and patreon
2019-10-05 19:29:35 -04:00
Fernando Sahmkow 47ccfabe18 Merge pull request #2944 from lioncash/ast
video_core/shader: Minor changes
2019-10-05 12:02:51 -04:00
Lioncash f883cd4f0e video_core/control_flow: Eliminate variable shadowing warnings 2019-10-05 09:14:27 -04:00
Lioncash 25702b6256 video_core/control_flow: Eliminate pessimizing moves
These can inhibit the ability of a compiler to perform RVO.
2019-10-05 09:14:27 -04:00
Lioncash d82b181d44 video_core/ast: Unindent most of IsFullyDecompiled() by one level 2019-10-05 09:14:27 -04:00
Lioncash 6c41d1cd7e video_core/ast: Make ShowCurrentState() take a string_view instead of std::string
Allows the function to be non-allocating in terms of the output string.
2019-10-05 09:14:27 -04:00
Lioncash 3c54edae24 video_core/ast: Eliminate variable shadowing warnings 2019-10-05 09:14:26 -04:00
Lioncash 5a0a9c7449 video_core/ast: Replace std::string with a constexpr std::string_view
Same behavior, but without the need to heap allocate
2019-10-05 09:14:26 -04:00
Lioncash 3a20d9734f video_core/ast: Default the move constructor and assignment operator
This is behaviorally equivalent and also fixes a bug where some members
weren't being moved over.
2019-10-05 09:14:26 -04:00
Lioncash 43503a69bf video_core/{ast, expr}: Organize forward declaration
Keeps them alphabetically sorted for readability.
2019-10-05 09:14:26 -04:00
Lioncash 50ad745585 video_core/expr: Supply operator!= along with operator==
Provides logical symmetry to the interface.
2019-10-05 09:14:26 -04:00
Lioncash 8eb1398f8d video_core/{ast, expr}: Use std::move where applicable
Avoids unnecessary atomic reference count increments and decrements.
2019-10-05 09:14:23 -04:00
Lioncash 8e0c80f269 video_core/ast: Supply const accessors for data where applicable
Provides const equivalents of data accessors for use within const
contexts.
2019-10-05 08:22:03 -04:00
David 3728bbc22a Merge pull request #2888 from FernandoS27/decompiler2
Shader_IR: Implement a full control flow decompiler for the shader IR.
2019-10-05 21:52:20 +10:00
Zach Hilman 57fe7fdec0 qt: Change titlebar formatting 2019-10-05 00:10:04 -04:00
Zach Hilman 3d4a0b94e3 common: Add additional SCM revision fields 2019-10-05 00:09:49 -04:00
Zach Hilman d45ad75404 ci: Add version counter variable 2019-10-05 00:09:11 -04:00
bunnei 0a662d009b Merge pull request #2917 from FernandoS27/fermi-deduction-2
TextureCache: Add the ability to deduce if two textures are depth on blit.
2019-10-04 20:12:01 -04:00
ReinUsesLisp 25ee892d5e audio/audout_u: Change formatting for old clang-format versions 2019-10-04 23:51:56 +00:00
ReinUsesLisp e1afeec76d yuzu/game_list_worker: Silence warnings 2019-10-04 23:41:22 +00:00
ReinUsesLisp f297e9ff22 yuzu/game_list: Silence -Wswitch and -Wunused-variable 2019-10-04 23:41:22 +00:00
ReinUsesLisp 2b9b695fa7 yuzu/configure_service: Silence -Wswitch 2019-10-04 23:41:22 +00:00
ReinUsesLisp e03f46fb0e yuzu_tester: Remove unused variable 2019-10-04 23:41:22 +00:00
ReinUsesLisp 8d0b1a957e service/nvdrv: Silence -Wswitch 2019-10-04 23:41:22 +00:00
ReinUsesLisp 5c907f85fc service/nfp: Silence -Wunused and -Wswitch 2019-10-04 23:41:22 +00:00
ReinUsesLisp 0759df0aff service/hid: Silence -Wunused and -Wswitch 2019-10-04 23:41:22 +00:00
ReinUsesLisp ab6f8d8a1e service/am: Silence -Wreorder 2019-10-04 23:41:21 +00:00
ReinUsesLisp 634c6e24b0 service/hid: Remove unused system reference 2019-10-04 23:41:21 +00:00
ReinUsesLisp 1dbd22e695 service/friend: Remove unused field 2019-10-04 23:41:21 +00:00
ReinUsesLisp 99db7d23dd service/filesystem: Silence -Wunused-variable 2019-10-04 23:41:21 +00:00
ReinUsesLisp 8566096794 service/bcat: Silence -Wreorder and -Wunused 2019-10-04 23:41:21 +00:00
ReinUsesLisp 87e7cc2d5a service/audio: Silence -Wunused 2019-10-04 23:28:34 +00:00
ReinUsesLisp aacb473aa2 service/apm: Silence -Wunused and -Wreorder 2019-10-04 23:28:34 +00:00
ReinUsesLisp f4417eab8f common/file_util: Silence -Wswitch 2019-10-04 23:28:34 +00:00
Fernando Sahmkow ab47a660c8 Texture_Cache: Blit Deduction corrections and simplifications. 2019-10-04 18:53:47 -04:00
Fernando Sahmkow 2036504a82 TextureCache: Add the ability to deduce if two textures are depth on blit. 2019-10-04 18:53:46 -04:00
Fernando Sahmkow e6eae4b815 Shader_ir: Address feedback 2019-10-04 18:52:57 -04:00
Fernando Sahmkow 3c09d9abe6 Shader_Ir: Address Feedback and clang format. 2019-10-04 18:52:57 -04:00
Fernando Sahmkow 507a9c6a40 vk_shader_decompiler: Correct Branches inside conditionals. 2019-10-04 18:52:56 -04:00
Fernando Sahmkow 000ad558dd vk_shader_decompiler: Clean code and be const correct. 2019-10-04 18:52:55 -04:00
Fernando Sahmkow 7c756baa77 Shader_IR: clean up AST handling and add documentation. 2019-10-04 18:52:55 -04:00
Fernando Sahmkow 5ea740beb5 Shader_IR: Correct OutwardMoves for Ifs 2019-10-04 18:52:54 -04:00
Fernando Sahmkow 100a4bd988 vk_shader_compiler: Don't enclose branches with if(true) to avoid crashing AMD 2019-10-04 18:52:54 -04:00
Fernando Sahmkow 189a50bc2a gl_shader_decompiler: Refactor and address feedback. 2019-10-04 18:52:53 -04:00
Fernando Sahmkow b3c46d6948 Shader_IR: corrections and clang-format 2019-10-04 18:52:53 -04:00
Fernando Sahmkow 466cd52ad4 vk_shader_compiler: Correct SPIR-V AST Decompiling 2019-10-04 18:52:52 -04:00
Fernando Sahmkow 2e9a810423 Shader_IR: allow else derivation to be optional. 2019-10-04 18:52:52 -04:00
Fernando Sahmkow ca9901867e vk_shader_compiler: Implement the decompiler in SPIR-V 2019-10-04 18:52:51 -04:00
Fernando Sahmkow 0366c18d87 Shader_IR: mark labels as unused for partial decompile. 2019-10-04 18:52:51 -04:00
Fernando Sahmkow 47e4f6a52c Shader_Ir: Refactor Decompilation process and allow multiple decompilation modes. 2019-10-04 18:52:50 -04:00
Fernando Sahmkow 38fc995f6c gl_shader_decompiler: Implement AST decompiling 2019-10-04 18:52:50 -04:00
Fernando Sahmkow 6fdd501113 shader_ir: Declare Manager and pass it to appropiate programs. 2019-10-04 18:52:49 -04:00
Fernando Sahmkow 8be6e1c522 shader_ir: Corrections to outward movements and misc stuffs 2019-10-04 18:52:48 -04:00
Fernando Sahmkow 4fde66e609 shader_ir: Add basic goto elimination 2019-10-04 18:52:48 -04:00
Fernando Sahmkow c17953978b shader_ir: Initial Decompile Setup 2019-10-04 18:52:47 -04:00
Rodrigo Locatti d633397883 Merge pull request #2941 from FernandoS27/fix-master
SDL: Fix missing header
2019-10-04 22:50:15 +00:00
Fernando Sahmkow 678d9ccad6 SDL: Fix missing header
This fixes linux and mingw builds.
2019-10-04 18:14:11 -04:00
bunnei 94c34f23d7 Merge pull request #2896 from FearlessTobi/port-4950
Port citra-emu/citra#4950: "Add FPS to SDL title bar"
2019-10-04 15:51:03 -04:00
bunnei 7fbaf62bac Merge pull request #2936 from VPeruS/use-isallzeroarray
[crypto] Use IsAllZeroArray helper function
2019-10-04 15:44:35 -04:00
bunnei accdb84993 Merge pull request #2940 from lioncash/zlib
externals: Track mainline zlib as a submodule
2019-10-04 15:44:13 -04:00
Lioncash e29492d114 CMakeLists: Make libzip excluded from the ALL target
Likewise, we also only want to link in the libraries that we actually
make use of (so we don't need to worry about linking in test targets).
2019-10-04 05:02:01 -04:00
Lioncash 80bdb44ead externals: Use upstream zlib
We don't need to depend on a custom fork for this. We can add the
library as is, and then make it excluded from the ALL target, so we only
link in the libraries that we actually make use of.
2019-10-04 05:01:57 -04:00
bunnei c818728513 Merge pull request #2898 from FearlessTobi/port-4004
Port citra-emu/citra#4004: "qt_themes: add two colorful themes"
2019-10-03 21:34:40 -04:00
David 9aac7fbc22 Merge pull request #2539 from DarkLordZach/bcat
bcat: Implement BCAT service and connect to yuzu Boxcat server
2019-10-03 19:06:13 +10:00
bunnei 6bfabdedfd Merge pull request #2937 from DarkLordZach/azure-msvc
ci: Add windows MSVC builds to patreon and mainline pipelines
2019-10-02 19:28:03 -04:00
Zach Hilman a86e52a375 ci: Correct mainline release dependency 2019-10-02 18:54:05 -04:00
Zach Hilman 53be058e74 ci: Add Mainline tagline 2019-10-02 18:51:21 -04:00
Zach Hilman d648cd562a ci: Use MSVC windows for patreon 2019-10-02 18:23:09 -04:00
Zach Hilman bfa60e2d4e ci: Use MSVC windows for mainline 2019-10-02 17:58:52 -04:00
Zach Hilman 514b74a098 ci: Add MSVC build template 2019-10-02 17:58:33 -04:00
Zach Hilman 49344111cc ci: Add Windows MSVC package script 2019-10-02 17:53:53 -04:00
Zach Hilman c56822a405 ci: Fix unset environment variable bug 2019-10-02 17:53:37 -04:00
vperus 29b1d0db0f [crypto] Use IsAllZeroArray helper function 2019-10-02 19:20:45 +03:00
Zach Hilman e55d086cc9 qt: Add service dialog 2019-10-02 08:35:43 -04:00
bunnei f8ce672b67 Merge pull request #2932 from DarkLordZach/azure-gitpages
ci: Iterate through GitHub pages for pull requests
2019-10-01 23:22:49 -04:00
Zach Hilman 0e58bfedfd ci: Check additional pages on GitHub 2019-10-01 21:54:26 -04:00
Zach Hilman 7d46416a16 ci: Iterate through GitHub pages for pull requests 2019-10-01 21:38:09 -04:00
bunnei 5e677a3178 Merge pull request #2904 from ogniK5377/better-signal-hid
Signal styleset changes at a better time
2019-10-01 10:58:02 -04:00
bunnei c26e9c4cd1 Merge pull request #2924 from MysticExile/readme-2
Remove AppVeyor status from the readme.md
2019-10-01 10:57:19 -04:00
bunnei 80d6abc08b Merge pull request #2929 from DarkLordZach/azure-patreon-merge-extra
ci: Merge patreon tagged PRs from public repository during merge step.
2019-10-01 10:56:50 -04:00
Zach Hilman 5d86c52a3a boxcat: Use updated game-asset API URL and tags 2019-10-01 09:13:31 -04:00
Zach Hilman 19c466dfb1 bcat: Add FSC accessors for BCAT data
Ports BCAT to use FSC interface
2019-10-01 09:13:09 -04:00
Zach Hilman bcf1eafb8b boxcat: Implement events global field 2019-09-30 17:28:23 -04:00
Zach Hilman 2d410ddf4d bcat: Implement DeliveryCacheProgressImpl structure
Huge thanks to lioncash for re-ing this for me.
2019-09-30 17:27:23 -04:00
Zach Hilman 92b70a3bf9 boxcat: Use Etag header names for file digest 2019-09-30 17:27:23 -04:00
Zach Hilman e8183f9ef0 boxcat: Add downloading and client for launch parameter data 2019-09-30 17:27:23 -04:00
Zach Hilman b8ce87103d bcat: Add backend function for BCAT Indirect (launch parameter)
Returns the data that should be returned by PopLaunchParameter kind=ApplicationSpecific.
2019-09-30 17:27:23 -04:00
Zach Hilman ea17b294ea bcat: Expose CreateBackendFromSettings helper function 2019-09-30 17:27:23 -04:00
Zach Hilman fe8c7e66e2 am: Unstub PopLaunchParameter and add bcat connection for app-specific data
Previously we were simply returning the account-preselect structure all times but if passed with a different mode the game expects application-specific data. This also adds a hook for BCAT into this allowing us to send the launch parameter through bcat,
2019-09-30 17:27:23 -04:00
Zach Hilman 02f8f1bb3e configure_service: Allow Qt to open external links 2019-09-30 17:26:10 -04:00
Zach Hilman d8bcb1e973 cmake: Add cmake option to build Boxcat backend
Default enabled
2019-09-30 17:26:10 -04:00
Zach Hilman f0551aef09 yuzu: Add UI tab to configure BCAT services
Also displays current events if boxcat is selected.
2019-09-30 17:26:10 -04:00
Zach Hilman 102db206e0 bcat: Implement cmd 90201 ClearDeliveryCacheStorage
Takes a title ID and simply deletes all the data for that title ID's bcat. Invokes the respective backend command.
2019-09-30 17:23:26 -04:00
Zach Hilman 1bde5a3c6a bcat: Implement cmd 30100 SetPassphrase
Takes a title ID and passphrase (0x40 byte string) and passes it to the backend.
2019-09-30 17:23:26 -04:00
Zach Hilman 86773a7f08 bcat: Implement cmd RequestSyncDeliveryCache and variant
Variant also supports only updating a single directory. These just both invoke backend commands.
2019-09-30 17:23:26 -04:00
Zach Hilman cb7c96b96a bcat: Implement IDeliveryCacheProgressService commands
Used to query completion status and events for the current delivery task.
2019-09-30 17:23:26 -04:00
Zach Hilman f352ad5c93 bcat: Implement IDeliveryCacheFileService commands
Used to read the contents of files and access their metadata.
2019-09-30 17:23:26 -04:00
Zach Hilman 8812018c1d bcat: Implement IDeliveryCacheDirectoryService commands
Used to list and get directories at the root level.
2019-09-30 17:23:26 -04:00
Zach Hilman 862131ead9 bcat: Implement IDeliveryCacheStorageService commands
Used to create subclasses to manage files and directories and to list directories.
2019-09-30 17:23:26 -04:00
Zach Hilman 78d146f907 bcat: Add commands to create IDeliveryCacheStorageService
Used to access contents of download.
2019-09-30 17:23:26 -04:00
Zach Hilman 68658a8385 module: Create BCAT backend based upon Settings value on construction 2019-09-30 17:23:26 -04:00
Zach Hilman 2903f3524e bcat: Add BCAT backend for Boxcat service
Downloads content from yuzu servers and unpacks it into the temporary directory provided. Fully supports all Backend features except passphrase.
2019-09-30 17:21:53 -04:00
Zach Hilman 2c0b75a744 bcat: Add backend class to generify the functions of BCAT
Provides the most abstract simplified functions of BCAT as functions. Also includes a NullBackend class which is just a no-op.
2019-09-30 17:21:53 -04:00
Zach Hilman 647992e666 settings: Add option to set BCAT backend 2019-09-30 17:21:53 -04:00
Zach Hilman 532ec459b8 nifm: Signal to applications that internet access is available 2019-09-30 17:21:53 -04:00
Zach Hilman f6c53526b3 core/loader: Track the NSO build ID of the current process 2019-09-30 17:21:53 -04:00
Zach Hilman 943662dc3c applets: Add accessor for AppletFrontendSet
Allows other services to call applets without using LLE.
2019-09-30 17:20:49 -04:00
Zach Hilman f2073217a4 filesystem: Add getter for BCAT temporary directory 2019-09-30 17:20:49 -04:00
Zach Hilman c00ed8f4ff vfs: Add function to extract ZIP file into virtual filesystem 2019-09-30 17:18:38 -04:00
Zach Hilman 84b6059012 externals: Add zlib and libzip libraries to handle ZIP file parsing 2019-09-30 17:18:38 -04:00
Zach Hilman 4ea425d6cf ci: Correct arguments for mergebot script 2019-09-30 13:43:34 -04:00
Zach Hilman e6f3aad84e ci: Populate patreon PRs on public repository 2019-09-30 09:29:28 -04:00
bunnei e11afeb34d Merge pull request #2923 from yuzu-emu/revert-2574-dynarmic-jit-nullptr
Revert "arm_dynarmic: Check if jit is nullptr when preparing reschedule"
2019-09-29 21:54:36 -04:00
bunnei dc29919bbe Revert "arm_dynarmic: Check if jit is nullptr when preparing reschedule" 2019-09-29 21:54:19 -04:00
bunnei 28538bba9c Merge pull request #2574 from DarkLordZach/dynarmic-jit-nullptr
arm_dynarmic: Check if jit is nullptr when preparing reschedule
2019-09-29 21:44:10 -04:00
Ethan 4bd6196b0a Remove AppVeyor status 2019-09-29 22:32:44 +02:00
Rodrigo Locatti 8a2e96de2e Merge pull request #2909 from MysticExile/fmt-2
externals: Update fmt to master
2019-09-27 14:09:32 -03:00
Rodrigo Locatti c4a99944f2 Merge pull request #2911 from DarkLordZach/azure-patreon
ci: Add azure patreon pipeline
2019-09-26 19:30:33 -03:00
Zach Hilman 867e1db287 ci: Add two step patreon build pipeline 2019-09-26 09:42:22 -04:00
Zach Hilman 0ba7055873 ci: Add private mergebot CI templates 2019-09-26 09:29:33 -04:00
Zach Hilman 3f52bb5677 ci: Optionally prepend commit messages with environment variable 2019-09-26 09:29:15 -04:00
Zach Hilman ab8d122384 ci: Add private mergebot script
Uses Azure instead of GitHub
2019-09-26 09:28:59 -04:00
Ethan 3e5790de79 externals: Update fmt to master 2019-09-26 00:34:37 +02:00
bunnei da07373599 Merge pull request #2864 from ReinUsesLisp/fix-clang-decompiler
gl_shader_decompiler: Fix clang build issues
2019-09-25 17:46:02 -04:00
David Marcec 77fbf29047 Signal styleset changes at a better time
We should signal when a net controller is added and our event should be manual, not automatic.
2019-09-24 17:24:37 +10:00
ReinUsesLisp f926230ab1 gl_shader_decompiler: Add tailing return for HUnpack2 2019-09-24 01:03:59 -03:00
ReinUsesLisp 25bfaffdff gl_shader_decompiler: Fix clang build issues 2019-09-24 01:03:27 -03:00
bunnei 376f1a4432 Merge pull request #2869 from ReinUsesLisp/suld
shader/image: Implement SULD and fix SUATOM
2019-09-23 21:47:03 -04:00
James Rowe 2f5545f8de Merge pull request #2901 from DarkLordZach/mainline-title-bar
ci: Properly parse Azure CI details in title bar
2019-09-22 18:14:08 -06:00
Zach Hilman 921dae5e83 ci: Correct GitHub Release name 2019-09-22 19:06:48 -04:00
Zach Hilman 15805f4e01 ci: Determine build date via bash 2019-09-22 18:39:29 -04:00
Zach Hilman 851c5d67ae ci: Update to use date as build number 2019-09-22 17:23:47 -04:00
Zach Hilman 14248685af cmake: Add SCM detection for Azure 2019-09-22 17:23:10 -04:00
Zach Hilman cc3db2aa43 ci: Split mainline pipeline and add support for GitHub releases (#2900)
* ci: Add mock build alternative for fast testing

* ci: Always cache build

* ci: Extract steps to download build stage artifacts

* ci: Add template to release to GitHub

* ci: Add template to release to Azure Universal Artifacts

* ci: Split mainline to two pipelines
2019-09-22 20:01:29 +00:00
FearlessTobiandPengfei Zhu 855e7237ff qt_themes: add two colorful themes
These two colorful themes are based on the Default and Dark themes, and contain icons that are colored rather than black and white. These icons come from icons8.com and they have been slightly revised by me. I'm pretty sure I was licensed to use them for Citra.

Co-Authored-By: Pengfei Zhu <[email protected]>
2019-09-22 16:42:00 +02:00
David aaec1562f8 Merge pull request #2683 from DarkLordZach/lock-exit
am: Implement exit locking and self exit commands
2019-09-23 00:37:12 +10:00
David a9abf4e7f8 Merge pull request #2889 from FearlessTobi/adwsawdawd
Add missing include
2019-09-23 00:30:12 +10:00
Zach Hilman b835d76311 Merge pull request #2876 from ogniK5377/AcquireNpadStyleSetUpdateEventHandle-fix
AcquireNpadStyleSetUpdateEventHandle should have a separate event for each controller type
2019-09-22 10:06:48 -04:00
Zach Hilman b7725812ac Merge pull request #2877 from ogniK5377/framecount-rev7
Added frame_count for REV5 audio renderer
2019-09-22 10:05:44 -04:00
Zach Hilman 9f3bf6d157 main: Use const on all variable initializations 2019-09-22 10:02:07 -04:00
David e31c15606b Merge pull request #2895 from FearlessTobi/debug-logs
service/acc: Lower log severity from INFO to DEBUG
2019-09-22 23:50:49 +10:00
jroweboy 64dbc92b61 Add FPS to SDL title bar
Also fix a small issue with incorrect shutdown ordering in SDL.
Previously the system would still be running so the telemetry task
didn't launch and detached_tasks would assert(count == 0)
2019-09-22 15:49:39 +02:00
FearlessTobi e22e0eb8d7 Add missing include 2019-09-22 15:47:21 +02:00
Fernando Sahmkow a755f24369 Merge pull request #2873 from ogniK5377/new-ioctls
Initial implementation of Ioctl2 & Ioctl3
2019-09-22 09:45:29 -04:00
David Marcec 59fd910355 removed comment 2019-09-22 23:42:52 +10:00
David Marcec 654427d4d0 Rebased 2019-09-22 23:42:41 +10:00
Fernando Sahmkow 4ace69de9c Merge pull request #2884 from ogniK5377/deglobal-sys-services
Remove usage of System::CurrentInterface() from most services
2019-09-22 09:38:13 -04:00
FearlessTobi 82979296d2 service/acc: Lower log severity from INFO to DEBUG
According to ogniK, this should have always been Debug and not Info.
2019-09-22 15:15:07 +02:00
David 9d69206cd0 Merge pull request #2870 from FernandoS27/multi-draw
Implement a MME Draw commands Inliner and correct host instance drawing
2019-09-22 23:13:02 +10:00
Fernando Sahmkow 822ca65d69 Merge pull request #2891 from FearlessTobi/rod-tex
video_core: Implement RGBX16F and lower Surface Copy log severity
2019-09-22 09:11:28 -04:00
David 3bfba23362 Merge pull request #2867 from ReinUsesLisp/configure-framebuffers-clean
gl_rasterizer: Remove unused code paths from ConfigureFramebuffers
2019-09-22 23:10:07 +10:00
Fernando Sahmkow 68f5aff64f Maxwell3D: Corrections and refactors to MME instance refactor 2019-09-22 07:23:13 -04:00
David Marcec 9513abbb0a removed unneeded semicolon 2019-09-22 18:50:34 +10:00
David cf1e4770f9 Merge pull request #2893 from ogniK5377/revert-dlz
Revert "Merge pull request #2709 from DarkLordZach/oss-ext-fonts-1"
2019-09-22 18:09:55 +10:00
David Marcec d961d5479e Revert "Merge pull request #2709 from DarkLordZach/oss-ext-fonts-1"
This reverts commit fa1c60c33e, reversing
changes made to e34899067b.
2019-09-22 17:47:25 +10:00
David Marcec e73ac40eaa Removed reference to core timing to nvflinger and used system instead 2019-09-22 16:55:15 +10:00
David Marcec aed884d121 marked controller constructors as explicit 2019-09-22 16:41:38 +10:00
David Marcec bd1c4ec9a0 Rebase 2019-09-22 16:41:34 +10:00
David Marcec fcdbf0bc53 Rebase 2019-09-22 16:40:58 +10:00
David Marcec 4d220964df Deglobalize System: Vi 2019-09-22 16:38:03 +10:00
David Marcec 2c6e4ce0ad Deglobalize System: Time 2019-09-22 16:38:02 +10:00
David Marcec 36a97dd8a2 Rebase 2019-09-22 16:37:59 +10:00
David Marcec d6e830d877 Deglobalize System: NvFlinger 2019-09-22 16:35:51 +10:00
David Marcec f21ab654db Rebase 2019-09-22 16:35:43 +10:00
David Marcec 8d3ff2b127 Deglobalize System: Nim 2019-09-22 16:30:34 +10:00
David Marcec ad53dc0106 Deglobalize System: Nifm 2019-09-22 16:30:33 +10:00
David Marcec 8df2a98f75 Deglobalize System: NFP 2019-09-22 16:30:32 +10:00
David Marcec 482a03f8a5 Deglobalize System: LDR 2019-09-22 16:30:31 +10:00
David Marcec 07823b61a1 Deglobalize System: IRS 2019-09-22 16:30:30 +10:00
David Marcec 28181919a6 Deglobalize System: Hid 2019-09-22 16:30:29 +10:00
David Marcec a9e9570d84 Deglobalize System: Friend 2019-09-22 16:30:27 +10:00
David Marcec a40e5b2def Deglobalize System: Fatal 2019-09-22 16:30:25 +10:00
David Marcec c33faabb27 Deglobalize System: Btm 2019-09-22 16:30:24 +10:00
David Marcec f2b61ff073 Deglobalize System: Btdrv 2019-09-22 16:30:20 +10:00
David Marcec 7da8e3f812 Deglobalize System: Aoc 2019-09-22 16:30:17 +10:00
David Marcec 2dbfac652e Deglobalize System: Am 2019-09-22 16:30:14 +10:00
David 9187350b32 Merge pull request #2535 from DarkLordZach/cheat-v2
cheat_engine: Use Atmosphere's Cheat VM and fix cheat crash
2019-09-22 16:24:42 +10:00
David fa1c60c33e Merge pull request #2709 from DarkLordZach/oss-ext-fonts-1
system_archive: Move shared font data to system_archive and fix extended font data
2019-09-22 16:09:22 +10:00
Zach Hilman 2a4730cbee pl_u: Use kernel physical memory 2019-09-21 23:00:22 -04:00
Zach Hilman 60c2e9e675 qt: Prompt user for confirmation if exit lock is active 2019-09-21 22:46:57 -04:00
Zach Hilman 2bddc03468 dmnt_cheat_vm: Default initialize structure values 2019-09-21 22:43:49 -04:00
David e34899067b Merge pull request #2886 from MysticExile/dynarmic
externals: Update dynarmic to master
2019-09-22 12:41:50 +10:00
David Marcec 6325c3044c server side clang format fix2 2019-09-22 12:26:03 +10:00
Zach Hilman e58e3719d8 am: Implement ISelfController ExitLock commands 2019-09-21 22:25:18 -04:00
Zach Hilman a7fda84902 am: Implement ISelfController Exit
Closes the current application.
2019-09-21 22:25:18 -04:00
Zach Hilman 4c1c8801a5 am: Add RequestExit event to AppletMessageQueue
Tested against libnx, signals to games to begin cleanup.
2019-09-21 22:24:10 -04:00
Zach Hilman bbc1437188 core: Track system exit lock status
Used to determine if yuzu should confirm before pausing or stopping a game.
2019-09-21 22:23:44 -04:00
David Marcec d3783fcc52 Use clang-format provided by build server 2019-09-22 12:21:07 +10:00
Zach Hilman 885ea2de2a pl_u: Remove excess static qualifier 2019-09-21 22:17:30 -04:00
Zach Hilman 94afffe9e5 pl_u: Use OSS system archives if real archives don't exist 2019-09-21 21:50:41 -04:00
David a1b8e5d09a Merge pull request #2612 from DarkLordZach/prepo-new
prepo: Implement New, System, and Non-User variants of SaveReport
2019-09-22 11:49:36 +10:00
Zach Hilman 682174b112 dmnt_cheat_vm: Make Cheat VM compliant to code style 2019-09-21 21:45:05 -04:00
Zach Hilman 3e729c13cc core: Initialize cheats after load to avoid VMManager crash
This used to occur due to the VMManager being nullptr at the time cheats were registered (during load, but before it was done). This is bypassed by not accessing the VMManager for offset data until load is complete,
2019-09-21 21:45:05 -04:00
Zach Hilman 37850eeee5 core: Update RegisterCheatList for new VM 2019-09-21 21:45:05 -04:00
Zach Hilman a0055192fe patch_manager: Update cheat parsing for new VM 2019-09-21 21:45:05 -04:00
Zach Hilman c6becfc9f5 nso: Pass build ID directly
As opposed to converting to string and then back to hex array
2019-09-21 21:44:22 -04:00
Zach Hilman 7d41c1f523 cheat_engine: Move to memory and strip VM
This is to go with the Atmosphere VM port, now it just contains the callbacks needed for the interface between DmntCheatVm and yuzu, along with the cheat parsers.
2019-09-21 21:44:22 -04:00
Zach Hilman 12aa127df3 memory: Port Atmosphere's DmntCheatVm
This was done because the current VM contained many inaccuracies and this also allows cheats to have identical behavior between hardware and yuzu.
2019-09-21 21:44:22 -04:00
Zach Hilman 470466b31b log: Add logging class for Cheat Engine
This is better than just using something like Common.Filesystem or Common.Memory
2019-09-21 21:44:22 -04:00
David c9ccdfbeac Merge pull request #2430 from DarkLordZach/fs-controller
core: Implement FileSystemController to deglobalize FS services
2019-09-22 11:42:34 +10:00
Zach Hilman 7979ccd956 Merge pull request #2883 from ogniK5377/log-game
Log the current title id and game name which is booting
2019-09-21 21:34:19 -04:00
FearlessTobi 01fc969a5f Fix clang-format 2019-09-22 02:21:56 +02:00
FearlessTobi 366e900376 fermi_2d: Lower surface copy log severity to DEBUG 2019-09-22 02:18:57 +02:00
FearlessTobi 55d272efe6 video_core: Implement RGBX16F PixelFormat 2019-09-22 02:16:44 +02:00
Zach Hilman 8b857fc7c2 system_archive: Synthesize shared fonts system archives 2019-09-21 19:21:58 -04:00
Zach Hilman ae9604faba pl_u: Expose method to encrypt TTF to BFTTF 2019-09-21 19:21:58 -04:00
Zach Hilman 361a8fa318 externals: Move OSS font data to file_sys in core 2019-09-21 19:21:22 -04:00
Zach Hilman 8dd2e91427 prepo: Remove system global accessors 2019-09-21 19:17:45 -04:00
Zach Hilman daf9cd9358 prepo: Implement SaveReport New and System variants 2019-09-21 19:17:08 -04:00
Zach Hilman 787b191abf reporter: Differentiate between Old, New, and System play reports 2019-09-21 19:17:08 -04:00
Zach Hilman 038bcec111 configure_debug: Move reporting option to logging 2019-09-21 18:43:20 -04:00
MysticExileandMorph 2b514275ad Update README.md (#2871)
* Update README.md

* Adress feedback

* Add link to our compatability page

* Address feedback

Co-Authored-By: Morph <[email protected]>

* Fix spelling

Co-Authored-By: Morph <[email protected]>

* Small fix

* Removed the mention of a specific game

* fix
2019-09-21 17:37:21 -04:00
Rodrigo Locatti 9286976948 Merge pull request #2878 from FernandoS27/icmp
shader_ir: Implement ICMP
2019-09-21 18:06:07 -03:00
Zach Hilman ccd70819c2 config: Remove Dump options from configure_debug 2019-09-21 16:50:39 -04:00
Zach Hilman a49169e819 filesystem: Add const qualification to various accessors 2019-09-21 16:50:39 -04:00
Zach Hilman d4d38dd44d yuzu: Add UI to manage filesystem paths and sizes 2019-09-21 16:50:39 -04:00
Zach Hilman c182688ad6 core: Store FileSystemController in core 2019-09-21 16:50:39 -04:00
Zach Hilman 2590b5a9ea settings: Add options for managing gamecard emulation 2019-09-21 16:43:10 -04:00
Zach Hilman 918119ae1b settings: Add options for setting storage sizes 2019-09-21 16:43:10 -04:00
Zach Hilman c6ff4a6f4d yuzu: Port old usages of Filesystem namespace to FilesystemController 2019-09-21 16:43:10 -04:00
Zach Hilman faf628ad8d settings: Update LogSettings to show NAND/SDMC paths from FileUtil 2019-09-21 16:43:10 -04:00
Zach Hilman ccaafaccfc card_image: Add accessors for gamecard certificate
Used by fsp-srv/IDeviceOperator
2019-09-21 16:43:10 -04:00
Zach Hilman 77f9ecd32b card_image: Add functions to query gamecard update partition
Includes version and meta title ID, used by fsp-srv/IDeviceOperator
2019-09-21 16:43:10 -04:00
Zach Hilman e018a48460 content_archive: Add accessors for Rights ID and SDK Version 2019-09-21 16:43:10 -04:00
Zach Hilman b4164d295b partition_data_manager: Add accessor for decrypted PRODINFO partition 2019-09-21 16:43:10 -04:00
Zach Hilman 4b91057688 services: Pass FileSystemController as reference to services that need it 2019-09-21 16:43:10 -04:00
Zach Hilman 1b04b72653 am: Unstub IApplicationFunctions EnsureSaveData (20)
Creates a default save data for the application given a user ID.
2019-09-21 16:43:10 -04:00
Zach Hilman 43af31836e filesystem: Pass Size Getter functions to IFileSystem for sizes 2019-09-21 16:43:10 -04:00
Zach Hilman 721a92775d sdmc_factory: Add SD Card size getters 2019-09-21 16:43:10 -04:00
Zach Hilman e47b57a90f bis_factory: Add getters for NAND partition sizes 2019-09-21 16:43:10 -04:00
Zach Hilman 8abbc619a1 filesystem: Add FileSystemController to deglobalize FS services 2019-09-21 16:43:10 -04:00
Zach Hilman 0a8e540681 submisson_package: Fix edge case with improperly sized filenames
Prevents a crash if the filename is less than 9 characters long.
2019-09-21 16:43:10 -04:00
Zach Hilman 08c0783d34 sdmc_factory: Add accessor for SDMC Album directory 2019-09-21 16:43:10 -04:00
Zach Hilman 0084cceb20 sdmc_factory: Add accessor for SDMC PlaceholderCache 2019-09-21 16:43:10 -04:00
Zach Hilman 02b36b0eb5 sdmc_factory: Add accessor for content directory 2019-09-21 16:43:10 -04:00
Zach Hilman 49c44e3fae savedata_factory: Implement savedata creation and don't create dir on open
Matches hardware behavior and eliminates some nasty behavior we were doing that wasn't hw-accurate at all.
2019-09-21 16:43:10 -04:00
Zach Hilman 62d772eaed patch_manager: Add short-circuit edge-case to GetPatchVersionNames
If title ID is 0, there are no add ons, prevents wasting time looking for them.
2019-09-21 16:43:10 -04:00
Zach Hilman 06db4d94fd patch_manager: Add error checking to load dir to prevent crashes
Prevents a crash if the load dir would be nullptr, instead logs an error and returns appropriately.
2019-09-21 16:43:10 -04:00
Zach Hilman 9d9fc8a675 registered_cache: Process *.cnmt.nca files
Needed to use the RegisteredCache/PlaceholderCache on gamecards.
2019-09-21 16:43:10 -04:00
Zach Hilman 8500ca797f registered_cache: Implement PlaceholderCache to manage placeholder and installing content 2019-09-21 16:43:10 -04:00
Zach Hilman 256a50ad15 bis_factory: Fix mod loader edge-case with homebrew title IDs
Fixes a bug where homebrew that has a title ID with the update bit set can cause issues with the PatchManager
2019-09-21 16:43:10 -04:00
Zach Hilman b71bda45ae bis_factory: Add accessors for BIS placeholder caches 2019-09-21 16:43:10 -04:00
Zach Hilman 9bee885282 bis_factory: Add accessor for NAND Image Directory 2019-09-21 16:43:10 -04:00
Zach Hilman 4dae5a52a8 bis_factory: Add accessors for BIS content directories 2019-09-21 16:43:10 -04:00
Zach Hilman 3a1899d143 bis_factory: Add accessors for BIS partitions 2019-09-21 16:43:10 -04:00
ReinUsesLisp 44000971e2 gl_shader_decompiler: Use uint for images and fix SUATOM
In the process remove implementation of SUATOM.MIN and SUATOM.MAX as
these require a distinction between U32 and S32. These have to be
implemented with imageCompSwap loop.
2019-09-21 17:33:52 -03:00
ReinUsesLisp 675f23aedc shader/image: Implement SULD and remove irrelevant code
* Implement SULD as float.
* Remove conditional declaration of GL_ARB_shader_viewport_layer_array.
2019-09-21 17:32:48 -03:00
ReinUsesLisp 4de0f1e1c8 shader_bytecode: Add SULD encoding 2019-09-21 17:31:46 -03:00
Fernando Sahmkow 527b841c15 Shader_IR: ICMP corrections and fixes 2019-09-21 14:28:03 -04:00
Ethan 8b76444916 externals: Update dynarmic to master 2019-09-21 16:54:06 +02:00
David 97b8c9d2c3 Merge pull request #2885 from Hexagon12/port-4944
Port citra-emu/citra#4944: "Added Host CPU and OS to log"
2019-09-21 23:46:58 +10:00
pbarilla 8fd266a7c4 Added Host CPU and OS to log 2019-09-21 16:11:46 +03:00
David 183c445c30 Merge pull request #2806 from FearlessTobi/port-4882
Port citra-emu/citra#4882: "Add frametime logging for tracking performance over time"
2019-09-21 20:09:55 +10:00
David c7c8ffbc13 Merge pull request #2872 from FernandoS27/mem-gpu-opt
Core/Memory: Only FlushAndInvalidate GPU if the page is marked as RasterizerCachedMemory
2019-09-21 20:07:25 +10:00
David 25383b9ff2 Merge pull request #2576 from DarkLordZach/nsp-fix-1
nsp: Fix various errors with loading and processing of extracted NSPs
2019-09-21 20:05:24 +10:00
David c41365a56f Merge pull request #2866 from Morph1984/checkbox_fix
configure_input: Uncheck "Joycons Docked" when "Use Docked Mode" is checked
2019-09-21 19:58:22 +10:00
David 9ad42fb0cf Merge pull request #2868 from ReinUsesLisp/fix-mipmaps
maxwell_to_gl: Fix mipmap filtering
2019-09-21 19:57:09 +10:00
David Marcec b4db662053 Swapped TID and Game name to make it easier to parse 2019-09-21 17:43:18 +10:00
David Marcec 934ce530f6 Log the current title id and game name which is booting
Spit out a LOG_INFO of the current game name and it's title id in the log. This helps to read log files and figure out which games have which issues
2019-09-21 17:34:46 +10:00
David Marcec e9d19add7d Used revision 5 instead of 7, marked constexpr as static 2019-09-21 16:24:56 +10:00
David b9fd1e2bed Merge pull request #2879 from ogniK5377/trace-submitgpfifo
Mark KickOffPb & SubmitGPFIFO as trace
2019-09-21 16:22:39 +10:00
David 41836f3a17 Merge pull request #2880 from ogniK5377/drawarrays-trace
Mark DrawArrays as LOG_TRACE
2019-09-21 16:11:54 +10:00
David Marcec 01a4afee42 Mark DrawArrays as LOG_TRACE
There's no reason to clog logs with DrawArray.
2019-09-21 15:43:58 +10:00
David Marcec c2f966dbc1 Mark KickOffPb & SubmitGPFIFO as trace
These functions are not stubbed and are called fairly often. Due to the nature of how often they're called, we should keep them marked as LOG_TRACE instead of LOG_DEBUG or LOG_WARNING
2019-09-21 15:41:11 +10:00
bunnei bbe82d62b0 Merge pull request #2846 from ReinUsesLisp/fixup-viewport-index
gl_shader_decompiler: Avoid writing output attribute when unimplemented
2019-09-20 17:11:20 -04:00
bunnei 88d857499b Merge pull request #2855 from ReinUsesLisp/shfl
shader_ir/warp: Implement SHFL for Nvidia devices
2019-09-20 17:10:42 -04:00
Fernando Sahmkow 433e764bb0 Rasterizer: Correct introduced bug where a conditional render wouldn't stop a draw call from executing 2019-09-20 15:44:28 -04:00
Fernando Sahmkow 4b81d19a1a Shader_IR: Implement ICMP. 2019-09-19 20:56:29 -04:00
David Marcec b54cdeb284 Added frame_count for REV7 audio renderer
Added framecount
2019-09-20 10:42:09 +10:00
David Marcec 0740758b25 disable clang-format temp 2019-09-20 10:25:12 +10:00
Fernando Sahmkow 7761e44d18 Rasterizer: Refactor and simplify DrawBatch Interface. 2019-09-19 11:41:33 -04:00
Fernando Sahmkow d2ea592ddb Rasterizer: Address Feedback and conscerns. 2019-09-19 11:41:32 -04:00
Fernando Sahmkow c17655ce74 Rasterizer: Refactor draw calls, remove deadcode and clean up. 2019-09-19 11:41:31 -04:00
Fernando Sahmkow 7606da5611 VideoCore: Corrections to the MME Inliner and removal of hacky instance management. 2019-09-19 11:41:29 -04:00
Fernando Sahmkow ba02d564f8 Video Core: initial Implementation of InstanceDraw Packaging 2019-09-19 11:41:27 -04:00
David Marcec f9259c0383 Initial implementation of Ioctl2 & Ioctl3
Purpose of Ioctl2 and Ioctl3 is to prevent the passing of raw pointers through ioctls
2019-09-19 15:37:25 +10:00
Fernando Sahmkow 50259d7bdc Core/Memory: Only FlushAndInvalidate GPU if the page is marked as RasterizerCachedMemory
This commit avoids Invalidating and Flushing the GPU if the page is not
marked as a RasterizerCache Page.
2019-09-18 22:47:22 -04:00
bunnei b31880dc5e Merge pull request #2784 from ReinUsesLisp/smem
shader_ir: Implement shared memory
2019-09-18 16:26:05 -04:00
ReinUsesLisp 0526bf1895 shader_ir/warp: Implement SHFL 2019-09-17 17:44:07 -03:00
ReinUsesLisp 2dd6411753 maxwell_to_gl: Fix mipmap filtering
OpenGL texture filters follow GL_<texture_filter>_MIPMAP_<mipmap_filter>
but we were using them in the opposite way.
2019-09-17 03:32:24 -03:00
ReinUsesLisp af809b491e gl_rasterizer: Remove unused code paths from ConfigureFramebuffers 2019-09-17 02:50:42 -03:00
Morph 8d778c90e2 When docked mode is checked, uncheck "joycons docked" 2019-09-16 23:26:32 -04:00
Fernando Sahmkow 393cc3ef2f Merge pull request #2851 from ReinUsesLisp/srgb
renderer_opengl: Fix sRGB blits
2019-09-15 10:38:10 -04:00
Fernando Sahmkow b8b1747704 Merge pull request #2824 from ReinUsesLisp/mme
Revert "Revert #2466" and stub FirmwareCall 4
2019-09-15 06:17:04 -04:00
Rodrigo Locatti 193bfefce4 maxwell_3d: Update firmware 4 call stub commentary 2019-09-14 22:51:18 -03:00
Fernando Sahmkow daae327e86 Merge pull request #2857 from ReinUsesLisp/surface-srgb
video_core/surface: Add function to detect sRGB surfaces
2019-09-14 03:53:21 -04:00
Fernando Sahmkow 18fac59050 Merge pull request #2858 from ReinUsesLisp/vk-device
vk_device: Add miscellaneous features and minor style changes
2019-09-14 03:52:06 -04:00
bunnei ddfdeea3af Merge pull request #2667 from DarkLordZach/profile-editor
acc: Implement IProfileEditor interface and 'Store'/'StoreWithImage' commands
2019-09-14 00:46:18 -04:00
bunnei 3cc27e4dda Merge pull request #2757 from ReinUsesLisp/suatom
shader/image: Implement SUATOM and fix SUST
2019-09-14 00:45:51 -04:00
ReinUsesLisp 01d96e1136 vk_device: Add miscellaneous features and minor style changes
* Increase minimum Vulkan requirements
* Require VK_EXT_vertex_attribute_divisor
* Require depthClamp, samplerAnisotropy and largePoints features
* Search and expose VK_KHR_uniform_buffer_standard_layout
* Search and expose VK_EXT_index_type_uint8
* Search and expose native float16 arithmetics
* Track current driver with VK_KHR_driver_properties
* Query and expose SSBO alignment
* Query more image formats
* Improve logging overall
* Minor style changes
* Minor rephrasing of commentaries
2019-09-13 02:10:07 -03:00
ReinUsesLisp 78d078e183 externals: Update Vulkan headers 2019-09-13 01:54:00 -03:00
ReinUsesLisp 99e23bd0fd video_core/surface: Add function to detect sRGB surfaces
This is required for proper conversion to RGBA8_UNORM or RGBA8_SRGB
surfaces when a backend can target both native and converted ASTC.
2019-09-13 00:27:04 -03:00
ReinUsesLisp 6b997c8f7f renderer_opengl: Fix rebase mistake 2019-09-11 00:09:37 -03:00
ReinUsesLisp 36abf67e79 shader/image: Implement SUATOM and fix SUST 2019-09-10 20:22:31 -03:00
Fernando Sahmkow e60d281a01 gl_rasterizer: Correct sRGB Fix regression 2019-09-10 19:31:42 -03:00
ReinUsesLisp 78574746bd renderer_opengl: Fix sRGB blits
Removes the sRGB hack of tracking if a frame used an sRGB rendertarget
to apply at least once to blit the final texture as sRGB. Instead of
doing this apply sRGB if the presented image has sRGB.

Also enable sRGB by default on Maxwell3D registers as some games seem to
assume this.
2019-09-10 19:31:42 -03:00
bunnei 34b2c60f95 Merge pull request #2823 from ReinUsesLisp/shr-clamp
shader/shift: Implement SHR wrapped and clamped variants
2019-09-10 11:56:17 -04:00
bunnei c7ec7bc1f5 Merge pull request #2810 from ReinUsesLisp/mme-opt
maxwell_3d: Avoid moving macro_params
2019-09-10 11:55:45 -04:00
Fernando Sahmkow 434d0922dc Merge pull request #2759 from ReinUsesLisp/compute-images
gl_rasterizer: Bind images and samplers to compute
2019-09-10 08:57:05 -04:00
FearlessTobi d36a7a43c5 Address review comments 2019-09-10 12:57:45 +02:00
fearlessTobiandjroweboy 684b616f0d Add frametime logging for tracking performance over time
Co-Authored-By: jroweboy <[email protected]>
2019-09-10 12:44:19 +02:00
David 07a0242535 Merge pull request #2847 from VelocityRa/nro-nacp-fix
nro/ui: Show "Developer" field in Properties
2019-09-09 16:45:28 +10:00
David 1487153e06 Merge pull request #2716 from lioncash/hle-global
service/am: Remove usages of global system accessors
2019-09-09 16:44:41 +10:00
David 6f7b349461 Merge pull request #2763 from lioncash/map-phys
kernel/vm_manager: Minor cleanup
2019-09-09 16:43:54 +10:00
Nick Renieris bfc5bacecd nro: Implement ReadControlData
With this, the "Developer" field in the Properties for homebrew is now populated.

Signed-off-by: Nick Renieris <[email protected]>
2019-09-08 00:40:21 +03:00
ReinUsesLisp 17a9b0178d gl_shader_decompiler: Avoid writing output attribute when unimplemented 2019-09-06 15:02:12 -03:00
ReinUsesLisp 1f43e5296f gl_shader_decompiler: Keep track of written images and mark them as modified 2019-09-05 23:26:05 -03:00
ReinUsesLisp 7228e22098 texture_cache: Minor changes 2019-09-05 23:25:15 -03:00
ReinUsesLisp 322d0200c8 gl_rasterizer: Apply textures and images state 2019-09-05 20:35:51 -03:00
ReinUsesLisp 80ec2feee8 gl_rasterizer: Add samplers to compute dispatches 2019-09-05 20:35:51 -03:00
ReinUsesLisp 954fc02fdd gl_rasterizer: Minor code changes 2019-09-05 20:35:51 -03:00
ReinUsesLisp 04cdecb7a1 gl_state: Split textures and samplers into two arrays 2019-09-05 20:35:51 -03:00
ReinUsesLisp 6170337001 gl_rasterizer: Implement image bindings 2019-09-05 20:35:51 -03:00
ReinUsesLisp 5edf24b510 gl_state: Add support for glBindImageTextures 2019-09-05 20:35:51 -03:00
ReinUsesLisp 2424eefad2 texture_cache: Pass TIC to texture cache 2019-09-05 20:35:51 -03:00
ReinUsesLisp 3a450c1395 kepler_compute: Implement texture queries 2019-09-05 20:35:51 -03:00
ReinUsesLisp 2e5b5c2358 gl_rasterizer: Split SetupTextures 2019-09-05 20:35:51 -03:00
Fernando Sahmkow 4ee9949639 Merge pull request #2804 from ReinUsesLisp/remove-gs-special
gl_shader_cache: Remove special casing for geometry shaders
2019-09-05 16:03:46 -04:00
bunnei 03badbdd9b Merge pull request #2833 from ReinUsesLisp/fix-stencil
gl_rasterizer: Fix stencil testing
2019-09-05 15:27:31 -04:00
ReinUsesLisp 0f7b813d65 gl_shader_decompiler: Implement shared memory 2019-09-05 01:40:24 -03:00
ReinUsesLisp 4de04eba39 shader_ir: Implement LD_S
Loads from shared memory.
2019-09-05 01:38:37 -03:00
ReinUsesLisp f17415d431 shader_ir: Implement ST_S
This instruction writes to a memory buffer shared with threads within
the same work group. It is known as "shared" memory in GLSL.
2019-09-05 01:38:37 -03:00
Lioncash d1abe8e92a service/am: Remove usages of global system accessors
Avoids the use of global accessors, removing the reliance on global
state. This also makes dependencies explicit in the interface, as
opposed to being hidden
2019-09-04 20:38:43 -04:00
Lioncash f763e23083 kernel/vm_manager: Correct doxygen comment parameter tags for MapPhysicalMemory/UnmapPhysicalMemory
Corrects the parameter names within the doxygen comments so that they
resolve properly.
2019-09-04 19:58:55 -04:00
Lioncash b0da7e4262 kernel/vm_manager: Move variables closer to usage spots in MapPhysicalMemory/UnmapPhysicalMemory
Narrows the scope of variables down to where they're only necessary.
2019-09-04 19:58:52 -04:00
ReinUsesLisp 7bbc98cfc3 gl_rasterizer: Fix stencil testing
* Fix stencil dirty flags tracking when stencil is disabled
* Attach stencil on clears (previously it only attached depth)
* Attach stencil on drawing regardless of stencil testing being enabled
2019-09-04 01:59:09 -03:00
ReinUsesLisp 5f309b88db Revert "Revert #2466" and stub FirmwareCall 4 2019-09-04 01:55:45 -03:00
ReinUsesLisp 77ef4fa907 shader/shift: Implement SHR wrapped and clamped variants
Nvidia defaults to wrapped shifts, but this is undefined behaviour on
OpenGL's spec. Explicitly mask/clamp according to what the guest shader
requires.
2019-09-04 01:55:24 -03:00
ReinUsesLisp 701dedcfad maxwell_3d: Avoid moving macro_params 2019-09-04 01:55:01 -03:00
ReinUsesLisp 42e1bb6d46 gl_shader_cache: Remove special casing for geometry shaders
Now that ProgramVariants holds the primitive topology we no longer need
to keep track of individual geometry shaders topologies.
2019-09-04 01:54:43 -03:00
Lioncash 96cc9a9279 kernel/vm_manager: Correct behavior in failure case of UnmapPhysicalMemory()
If an unmapping operation fails, we shouldn't be decrementing the amount
of memory mapped and returning that the operation was successful. We
should actually be returning the error code in this case.
2019-08-29 19:29:43 -04:00
Lioncash 56c6f767ae kernel/vm_manager: Reserve memory ahead of time for slow path in MergeAdjacentVMA
Avoids potentially expensive (depending on the size of the memory block)
allocations by reserving the necessary memory before performing both
insertions. This avoids scenarios where the second insert may cause a
reallocation to occur.
2019-08-29 19:29:43 -04:00
Lioncash a43ee8d752 kernel/vm_manager: std::move shared_ptr instance in MergeAdjacentVMA
Avoids an unnecessary atomic reference count increment and decrement.
2019-08-29 19:28:58 -04:00
Lioncash 785c4946dd kernel/vm_manager: Deduplicate iterator creation in MergeAdjacentVMA
Avoids needing to read the same long sequence of code in both code
paths. Also makes it slightly nicer to read and debug, as the locals
will be able to be shown in the debugger.
2019-08-29 19:28:57 -04:00
Lioncash 70485e690b kernel/vm_manager: Simplify some std::vector constructor calls
Same behavior, one less magic constant to read.
2019-08-29 19:28:04 -04:00
Lioncash 3f695333cd kernel/vm_manager: Simplify some assertion messages
Assertions already log out the function name, so there's no need to
manually include the function name in the assertion strings.
2019-08-29 19:26:42 -04:00
Zach Hilman 471b2a4211 acc_su: Implement GetProfileEditor (205)
Takes a UUID of a user and provides and interface that allows RW access to user data/settings.
2019-07-03 07:57:41 -05:00
Zach Hilman 812fb30821 acc: Implement IProfileEditor-specific commands 'Store' and 'StoreWithImage'
Verified with IDA
2019-07-03 07:57:03 -05:00
Zach Hilman 02560d6482 profile_manager: Add setter for ProfileBase and ProfileData
Needed by IProfileEditor 'Store' and 'StoreWithImage'
2019-07-03 07:56:28 -05:00
Zach Hilman 39f6d57c34 acc: Add IProfileCommon for IProfile and IProfileEditor
Since 2/3 of the commands are shared, this is likely how its done on HW.
2019-07-03 07:55:54 -05:00
Zach Hilman b957a4862f nsp: Correct status codes for extracted NSPs
Avoids all extracted NSPs being marked as error file type because they don't have program NCAs.
2019-06-10 00:21:41 -04:00
Zach Hilman 1c75945dc4 nsp: Use title ID from NPDM metadata for extracted type NSPs
Avoids 0 being used as title ID for all extracted NSPs.
2019-06-10 00:19:23 -04:00
Zach Hilman 425cdf946c arm_dynarmic: Check if jit is nullptr when preparing reschedule
Prevents crash with multiprocess loading.
2019-06-10 00:14:25 -04:00
348 changed files with 14365 additions and 3563 deletions

No files matched your search

+3 -2
View File
@@ -5,10 +5,11 @@ cd /yuzu
ccache -s
mkdir build || true && cd build
cmake .. -G Ninja -DYUZU_USE_BUNDLED_UNICORN=ON -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON
cmake .. -G Ninja -DDISPLAY_VERSION=$1 -DYUZU_USE_BUNDLED_UNICORN=ON -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON
ninja
ccache -s
ctest -VV -C Release
# Ignore zlib's tests, since they aren't gated behind a CMake option.
ctest -VV -E "(example|example64)" -C Release
+1 -1
View File
@@ -2,4 +2,4 @@
mkdir -p "ccache" || true
chmod a+x ./.ci/scripts/linux/docker.sh
docker run -e ENABLE_COMPATIBILITY_REPORTING -e CCACHE_DIR=/yuzu/ccache -v $(pwd):/yuzu yuzuemu/build-environments:linux-fresh /bin/bash /yuzu/.ci/scripts/linux/docker.sh
docker run -e ENABLE_COMPATIBILITY_REPORTING -e CCACHE_DIR=/yuzu/ccache -v $(pwd):/yuzu yuzuemu/build-environments:linux-fresh /bin/bash /yuzu/.ci/scripts/linux/docker.sh $1
@@ -0,0 +1,45 @@
# Download all pull requests as patches that match a specific label
# Usage: python download-patches-by-label.py <Label to Match> <Root Path Folder to DL to>
import requests, sys, json, shutil, subprocess, os, traceback
org = os.getenv("PRIVATEMERGEORG", "yuzu-emu")
repo = os.getenv("PRIVATEMERGEREPO", "yuzu-private")
tagline = sys.argv[3]
user = sys.argv[1]
dl_list = {}
TAG_NAME = sys.argv[2]
def check_individual(repo_id, pr_id):
url = 'https://%sdev.azure.com/%s/%s/_apis/git/repositories/%s/pullRequests/%s/labels?api-version=5.1-preview.1' % (user, org, repo, repo_id, pr_id)
response = requests.get(url)
if (response.ok):
try:
js = response.json()
return any(tag.get('name') == TAG_NAME for tag in js['value'])
except:
return False
return False
def merge_pr(pn, ref):
print("Matched PR# %s" % pn)
print(subprocess.check_output(["git", "fetch", "https://%sdev.azure.com/%s/_git/%s" % (user, org, repo), ref, "-f"]))
print(subprocess.check_output(["git", "merge", "--squash", 'origin/' + ref.replace('refs/heads/','')]))
print(subprocess.check_output(["git", "commit", "-m\"Merge %s PR %s\"" % (tagline, pn)]))
def main():
url = 'https://%sdev.azure.com/%s/%s/_apis/git/pullrequests?api-version=5.1' % (user, org, repo)
response = requests.get(url)
if (response.ok):
js = response.json()
tagged_prs = filter(lambda pr: check_individual(pr['repository']['id'], pr['pullRequestId']), js['value'])
map(lambda pr: merge_pr(pr['pullRequestId'], pr['sourceRefName']), tagged_prs)
if __name__ == '__main__':
try:
main()
except:
traceback.print_exc(file=sys.stdout)
sys.exit(-1)
+12 -4
View File
@@ -1,7 +1,9 @@
# Download all pull requests as patches that match a specific label
# Usage: python download-patches-by-label.py <Label to Match> <Root Path Folder to DL to>
import requests, sys, json, urllib3.request, shutil, subprocess
import requests, sys, json, urllib3.request, shutil, subprocess, os
tagline = sys.argv[2]
http = urllib3.PoolManager()
dl_list = {}
@@ -12,17 +14,23 @@ def check_individual(labels):
return True
return False
try:
url = 'https://api.github.com/repos/yuzu-emu/yuzu/pulls'
def do_page(page):
url = 'https://api.github.com/repos/yuzu-emu/yuzu/pulls?page=%s' % page
response = requests.get(url)
if (response.ok):
j = json.loads(response.content)
if j == []:
return
for pr in j:
if (check_individual(pr["labels"])):
pn = pr["number"]
print("Matched PR# %s" % pn)
print(subprocess.check_output(["git", "fetch", "https://github.com/yuzu-emu/yuzu.git", "pull/%s/head:pr-%s" % (pn, pn), "-f"]))
print(subprocess.check_output(["git", "merge", "--squash", "pr-%s" % pn]))
print(subprocess.check_output(["git", "commit", "-m\"Merge PR %s\"" % pn]))
print(subprocess.check_output(["git", "commit", "-m\"Merge %s PR %s\"" % (tagline, pn)]))
try:
for i in range(1,30):
do_page(i)
except:
sys.exit(-1)
+1 -1
View File
@@ -13,7 +13,7 @@ echo '' >> /bin/cmd
chmod +x /bin/cmd
mkdir build || true && cd build
cmake .. -G Ninja -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_UNICORN=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release
cmake .. -G Ninja -DDISPLAY_VERSION=$1 -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_UNICORN=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release
ninja
# Clean up the dirty hacks
+1 -1
View File
@@ -2,4 +2,4 @@
mkdir -p "ccache" || true
chmod a+x ./.ci/scripts/windows/docker.sh
docker run -e CCACHE_DIR=/yuzu/ccache -v $(pwd):/yuzu yuzuemu/build-environments:linux-mingw /bin/bash -ex /yuzu/.ci/scripts/windows/docker.sh
docker run -e CCACHE_DIR=/yuzu/ccache -v $(pwd):/yuzu yuzuemu/build-environments:linux-mingw /bin/bash -ex /yuzu/.ci/scripts/windows/docker.sh $1
+32
View File
@@ -0,0 +1,32 @@
$GITDATE = $(git show -s --date=short --format='%ad') -replace "-",""
$GITREV = $(git show -s --format='%h')
$RELEASE_DIST = "yuzu-windows-msvc"
$MSVC_BUILD_ZIP = "yuzu-windows-msvc-$GITDATE-$GITREV.zip" -replace " ", ""
$MSVC_BUILD_PDB = "yuzu-windows-msvc-$GITDATE-$GITREV-debugsymbols.zip" -replace " ", ""
$MSVC_SEVENZIP = "yuzu-windows-msvc-$GITDATE-$GITREV.7z" -replace " ", ""
$env:BUILD_ZIP = $MSVC_BUILD_ZIP
$env:BUILD_SYMBOLS = $MSVC_BUILD_PDB
$env:BUILD_UPDATE = $MSVC_SEVENZIP
$BUILD_DIR = ".\build\bin\Release"
mkdir pdb
Get-ChildItem "$BUILD_DIR\" -Recurse -Filter "*.pdb" | Copy-Item -destination .\pdb
7z a -tzip $MSVC_BUILD_PDB .\pdb\*.pdb
rm "$BUILD_DIR\*.pdb"
mkdir $RELEASE_DIST
mkdir "artifacts"
Copy-Item "$BUILD_DIR\*" -Destination $RELEASE_DIST -Recurse
rm "$RELEASE_DIST\*.exe"
Get-ChildItem "$BUILD_DIR" -Recurse -Filter "yuzu*.exe" | Copy-Item -destination $RELEASE_DIST
Get-ChildItem "$BUILD_DIR" -Recurse -Filter "QtWebEngineProcess*.exe" | Copy-Item -destination $RELEASE_DIST
Copy-Item .\license.txt -Destination $RELEASE_DIST
Copy-Item .\README.md -Destination $RELEASE_DIST
7z a -tzip $MSVC_BUILD_ZIP $RELEASE_DIST\*
7z a $MSVC_SEVENZIP $RELEASE_DIST
Get-ChildItem . -Filter "*.zip" | Copy-Item -destination "artifacts"
Get-ChildItem . -Filter "*.7z" | Copy-Item -destination "artifacts"
+5
View File
@@ -0,0 +1,5 @@
steps:
- script: mkdir artifacts || echo 'X' > artifacts/T1.txt
- publish: artifacts
artifact: 'yuzu-$(BuildName)-$(BuildSuffix)'
displayName: 'Upload Artifacts'
+22
View File
@@ -0,0 +1,22 @@
parameters:
artifactSource: 'true'
cache: 'false'
version: ''
steps:
- script: mkdir build && cd build && cmake -G "Visual Studio 15 2017 Win64" --config Release -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_BUNDLED_UNICORN=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON -DDISPLAY_VERSION=${{ parameters['version'] }} .. && cd ..
displayName: 'Configure CMake'
- task: MSBuild@1
displayName: 'Build'
inputs:
solution: 'build/yuzu.sln'
maximumCpuCount: true
configuration: release
- task: PowerShell@2
displayName: 'Package Artifacts'
inputs:
targetType: 'filePath'
filePath: './.ci/scripts/windows/upload.ps1'
- publish: artifacts
artifact: 'yuzu-$(BuildName)-windows-msvc'
displayName: 'Upload Artifacts'
+8 -8
View File
@@ -1,20 +1,20 @@
parameters:
artifactSource: 'true'
cache: 'false'
version: ''
steps:
- task: DockerInstaller@0
displayName: 'Prepare Environment'
inputs:
dockerVersion: '17.09.0-ce'
- ${{ if eq(parameters.cache, 'true') }}:
- task: CacheBeta@0
displayName: 'Cache Build System'
inputs:
key: yuzu-v1-$(BuildName)-$(BuildSuffix)-$(CacheSuffix)
path: $(System.DefaultWorkingDirectory)/ccache
cacheHitVar: CACHE_RESTORED
- script: chmod a+x ./.ci/scripts/$(ScriptFolder)/exec.sh && ./.ci/scripts/$(ScriptFolder)/exec.sh
- task: CacheBeta@0
displayName: 'Cache Build System'
inputs:
key: yuzu-v1-$(BuildName)-$(BuildSuffix)-$(CacheSuffix)
path: $(System.DefaultWorkingDirectory)/ccache
cacheHitVar: CACHE_RESTORED
- script: chmod a+x ./.ci/scripts/$(ScriptFolder)/exec.sh && ./.ci/scripts/$(ScriptFolder)/exec.sh ${{ parameters['version'] }}
displayName: 'Build'
- script: chmod a+x ./.ci/scripts/$(ScriptFolder)/upload.sh && RELEASE_NAME=$(BuildName) ./.ci/scripts/$(ScriptFolder)/upload.sh
displayName: 'Package Artifacts'
+5 -1
View File
@@ -1,3 +1,6 @@
parameters:
version: ''
jobs:
- job: build
displayName: 'standard'
@@ -20,4 +23,5 @@ jobs:
- template: ./build-single.yml
parameters:
artifactSource: 'false'
cache: $(parameters.cache)
cache: $(parameters.cache)
version: $(parameters.version)
+4
View File
@@ -1,3 +1,6 @@
parameters:
version: ''
jobs:
- job: build_test
displayName: 'testing'
@@ -31,3 +34,4 @@ jobs:
parameters:
artifactSource: 'false'
cache: 'false'
version: $(parameters.version)
+47
View File
@@ -0,0 +1,47 @@
jobs:
- job: merge
displayName: 'pull requests'
steps:
- checkout: self
submodules: recursive
- template: ./mergebot-private.yml
parameters:
matchLabel: '$(BuildName)-merge'
matchLabelPublic: '$(PublicBuildName)-merge'
- task: ArchiveFiles@2
displayName: 'Package Source'
inputs:
rootFolderOrFile: '$(System.DefaultWorkingDirectory)'
includeRootFolder: false
archiveType: '7z'
archiveFile: '$(Build.ArtifactStagingDirectory)/yuzu-$(BuildName)-source.7z'
- task: PublishPipelineArtifact@1
displayName: 'Upload Artifacts'
inputs:
targetPath: '$(Build.ArtifactStagingDirectory)/yuzu-$(BuildName)-source.7z'
artifact: 'yuzu-$(BuildName)-source'
replaceExistingArchive: true
- job: upload_source
displayName: 'upload'
dependsOn: merge
steps:
- template: ./sync-source.yml
parameters:
artifactSource: 'true'
needSubmodules: 'true'
- script: chmod a+x $(System.DefaultWorkingDirectory)/.ci/scripts/merge/yuzubot-git-config.sh && $(System.DefaultWorkingDirectory)/.ci/scripts/merge/yuzubot-git-config.sh
displayName: 'Apply Git Configuration'
- script: git tag -a $(BuildName)-$(Build.BuildId) -m "yuzu $(BuildName) $(Build.BuildNumber) $(Build.DefinitionName)"
displayName: 'Tag Source'
- script: git remote add other $(GitRepoPushChangesURL)
displayName: 'Register Repository'
- script: git push --follow-tags --force other HEAD:$(GitPushBranch)
displayName: 'Update Code'
- script: git rev-list -n 1 $(BuildName)-$(Build.BuildId) > $(Build.ArtifactStagingDirectory)/tag-commit.sha
displayName: 'Calculate Release Point'
- task: PublishPipelineArtifact@1
displayName: 'Upload Release Point'
inputs:
targetPath: '$(Build.ArtifactStagingDirectory)/tag-commit.sha'
artifact: 'yuzu-$(BuildName)-release-point'
replaceExistingArchive: true
+30
View File
@@ -0,0 +1,30 @@
parameters:
matchLabel: 'dummy-merge'
matchLabelPublic: 'dummy-merge'
steps:
- script: mkdir $(System.DefaultWorkingDirectory)/patches && pip install requests urllib3
displayName: 'Prepare Environment'
- script: chmod a+x $(System.DefaultWorkingDirectory)/.ci/scripts/merge/yuzubot-git-config.sh && $(System.DefaultWorkingDirectory)/.ci/scripts/merge/yuzubot-git-config.sh
displayName: 'Apply Git Configuration'
- task: PythonScript@0
displayName: 'Discover, Download, and Apply Patches (Mainline)'
inputs:
scriptSource: 'filePath'
scriptPath: '.ci/scripts/merge/apply-patches-by-label.py'
arguments: '${{ parameters.matchLabelPublic }} $(MergeTaglinePublic) patches-public'
workingDirectory: '$(System.DefaultWorkingDirectory)'
- task: PythonScript@0
displayName: 'Discover, Download, and Apply Patches (Patreon Public)'
inputs:
scriptSource: 'filePath'
scriptPath: '.ci/scripts/merge/apply-patches-by-label.py'
arguments: '${{ parameters.matchLabel }} "$(MergeTaglinePrivate) Public" patches-mixed-public'
workingDirectory: '$(System.DefaultWorkingDirectory)'
- task: PythonScript@0
displayName: 'Discover, Download, and Apply Patches (Patreon Private)'
inputs:
scriptSource: 'filePath'
scriptPath: '.ci/scripts/merge/apply-patches-by-label-private.py'
arguments: '$(PrivateMergeUser) ${{ parameters.matchLabel }} "$(MergeTaglinePrivate) Private" patches-private'
workingDirectory: '$(System.DefaultWorkingDirectory)'
+1 -1
View File
@@ -11,5 +11,5 @@ steps:
inputs:
scriptSource: 'filePath'
scriptPath: '.ci/scripts/merge/apply-patches-by-label.py'
arguments: '${{ parameters.matchLabel }} patches'
arguments: '${{ parameters.matchLabel }} Tagged patches'
workingDirectory: '$(System.DefaultWorkingDirectory)'
+13
View File
@@ -0,0 +1,13 @@
steps:
- task: DownloadPipelineArtifact@2
displayName: 'Download Windows Release'
inputs:
artifactName: 'yuzu-$(BuildName)-windows-msvc'
buildType: 'current'
targetPath: '$(Build.ArtifactStagingDirectory)'
- task: DownloadPipelineArtifact@2
displayName: 'Download Linux Release'
inputs:
artifactName: 'yuzu-$(BuildName)-linux'
buildType: 'current'
targetPath: '$(Build.ArtifactStagingDirectory)'
+11
View File
@@ -0,0 +1,11 @@
steps:
- template: ./release-download.yml
- task: GitHubRelease@0
inputs:
action: 'create'
title: 'yuzu $(BuildName) #$(Build.BuildId)'
assets: '$(Build.ArtifactStagingDirectory)/*'
gitHubConnection: $(GitHubReleaseConnectionName)
repositoryName: '$(Build.Repository.Name)'
target: '$(Build.SourceVersion)'
tagSource: 'auto'
+10
View File
@@ -0,0 +1,10 @@
steps:
- template: ./release-download.yml
- task: UniversalPackages@0
displayName: Publish Artifacts
inputs:
command: publish
publishDirectory: '$(Build.ArtifactStagingDirectory)'
vstsFeedPublish: 'yuzu-$(BuildName)'
vstsFeedPackagePublish: 'main'
packagePublishDescription: 'Yuzu Windows and Linux Executable Packages'
+8
View File
@@ -0,0 +1,8 @@
trigger:
- master
stages:
- stage: merge
displayName: 'merge'
jobs:
- template: ./templates/merge.yml
+68
View File
@@ -0,0 +1,68 @@
trigger:
- master
variables:
DisplayVersion: $[counter(variables['DisplayPrefix'], 1)]
stages:
- stage: format
displayName: 'format'
jobs:
- job: format
displayName: 'clang'
pool:
vmImage: ubuntu-latest
steps:
- template: ./templates/format-check.yml
- stage: build
dependsOn: format
displayName: 'build'
jobs:
- job: build
displayName: 'standard'
pool:
vmImage: ubuntu-latest
strategy:
maxParallel: 10
matrix:
linux:
BuildSuffix: 'linux'
ScriptFolder: 'linux'
steps:
- template: ./templates/sync-source.yml
parameters:
artifactSource: $(parameters.artifactSource)
needSubmodules: 'true'
- template: ./templates/build-single.yml
parameters:
artifactSource: 'false'
cache: 'true'
version: $(DisplayVersion)
- stage: build_win
dependsOn: format
displayName: 'build-windows'
jobs:
- job: build
displayName: 'msvc'
pool:
vmImage: vs2017-win2016
steps:
- template: ./templates/sync-source.yml
parameters:
artifactSource: $(parameters.artifactSource)
needSubmodules: 'true'
- template: ./templates/build-msvc.yml
parameters:
artifactSource: 'false'
cache: 'true'
version: $(DisplayVersion)
- stage: release
displayName: 'Release'
dependsOn:
- build
- build_win
jobs:
- job: github
displayName: 'GitHub Release'
steps:
- template: ./templates/release-github.yml
-25
View File
@@ -1,25 +0,0 @@
trigger:
- master
stages:
- stage: merge
displayName: 'merge'
jobs:
- template: ./templates/merge.yml
- stage: format
dependsOn: merge
displayName: 'format'
jobs:
- job: format
displayName: 'clang'
pool:
vmImage: ubuntu-latest
steps:
- template: ./templates/format-check.yml
- stage: build
displayName: 'build'
dependsOn: format
jobs:
- template: ./templates/build-standard.yml
parameters:
cache: 'true'
+8
View File
@@ -0,0 +1,8 @@
trigger:
- master
stages:
- stage: merge
displayName: 'merge'
jobs:
- template: ./templates/merge-private.yml
+34
View File
@@ -0,0 +1,34 @@
trigger:
- master
variables:
DisplayVersion: $[counter(variables['DisplayPrefix'], 1)]
stages:
- stage: format
displayName: 'format'
jobs:
- job: format
displayName: 'clang'
pool:
vmImage: ubuntu-latest
steps:
- template: ./templates/format-check.yml
- stage: build
dependsOn: format
displayName: 'build'
jobs:
- job: build
displayName: 'windows-msvc'
pool:
vmImage: vs2017-win2016
steps:
- template: ./templates/sync-source.yml
parameters:
artifactSource: $(parameters.artifactSource)
needSubmodules: 'true'
- template: ./templates/build-msvc.yml
parameters:
artifactSource: 'false'
cache: $(parameters.cache)
version: $(DisplayVersion)
-19
View File
@@ -1,19 +0,0 @@
# Starter pipeline
# Start with a minimal pipeline that you can customize to build and deploy your code.
# Add steps that build, run tests, deploy, and more:
# https://aka.ms/yaml
trigger:
- master
pool:
vmImage: 'ubuntu-latest'
steps:
- script: echo Hello, world!
displayName: 'Run a one-line script'
- script: |
echo Add other tasks to build, test, and deploy your project.
echo See https://aka.ms/yaml
displayName: 'Run a multi-line script'
+6
View File
@@ -46,3 +46,9 @@
[submodule "sirit"]
path = externals/sirit
url = https://github.com/ReinUsesLisp/sirit
[submodule "libzip"]
path = externals/libzip
url = https://github.com/DarkLordZach/libzip
[submodule "zlib"]
path = externals/zlib
url = https://github.com/madler/zlib
+2
View File
@@ -21,6 +21,8 @@ option(YUZU_USE_BUNDLED_UNICORN "Build/Download bundled Unicorn" ON)
option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OFF)
option(YUZU_ENABLE_BOXCAT "Enable the Boxcat service, a yuzu high-level implementation of BCAT" ON)
option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
option(ENABLE_VULKAN "Enables Vulkan backend" ON)
+6
View File
@@ -83,9 +83,15 @@ set(HASH_FILES
"${VIDEO_CORE}/shader/decode/video.cpp"
"${VIDEO_CORE}/shader/decode/warp.cpp"
"${VIDEO_CORE}/shader/decode/xmad.cpp"
"${VIDEO_CORE}/shader/ast.cpp"
"${VIDEO_CORE}/shader/ast.h"
"${VIDEO_CORE}/shader/control_flow.cpp"
"${VIDEO_CORE}/shader/control_flow.h"
"${VIDEO_CORE}/shader/compiler_settings.cpp"
"${VIDEO_CORE}/shader/compiler_settings.h"
"${VIDEO_CORE}/shader/decode.cpp"
"${VIDEO_CORE}/shader/expr.cpp"
"${VIDEO_CORE}/shader/expr.h"
"${VIDEO_CORE}/shader/node.h"
"${VIDEO_CORE}/shader/node_helper.cpp"
"${VIDEO_CORE}/shader/node_helper.h"
+4 -3
View File
@@ -1,14 +1,15 @@
yuzu emulator
=============
[![Travis CI Build Status](https://travis-ci.org/yuzu-emu/yuzu.svg?branch=master)](https://travis-ci.org/yuzu-emu/yuzu)
[![AppVeyor CI Build Status](https://ci.appveyor.com/api/projects/status/77k97svb2usreu68?svg=true)](https://ci.appveyor.com/project/bunnei/yuzu)
[![Azure Mainline CI Build Status](https://dev.azure.com/yuzu-emu/yuzu/_apis/build/status/yuzu%20mainline?branchName=master)](https://dev.azure.com/yuzu-emu/yuzu/)
yuzu is an experimental open-source emulator for the Nintendo Switch from the creators of [Citra](https://citra-emu.org/).
It is written in C++ with portability in mind, with builds actively maintained for Windows, Linux and macOS. The emulator is currently only useful for homebrew development and research purposes.
It is written in C++ with portability in mind, with builds actively maintained for Windows and Linux. The emulator is capable of running several commercial games.
yuzu only emulates a subset of Switch hardware and therefore is generally only useful for running/debugging homebrew applications. yuzu can boot some games, to varying degrees of success.
yuzu only emulates a subset of Switch hardware and therefore most commercial games **do not** run at full speed or are not fully functional.
Do you want to check which games are compatible and which ones are not? Please visit our [Compatibility page](https://yuzu-emu.org/game/)!
yuzu is licensed under the GPLv2 (or any later version). Refer to the license.txt file included.
+6
View File
@@ -77,6 +77,12 @@ if (ENABLE_VULKAN)
add_subdirectory(sirit)
endif()
# zlib
add_subdirectory(zlib EXCLUDE_FROM_ALL)
# libzip
add_subdirectory(libzip EXCLUDE_FROM_ALL)
if (ENABLE_WEB_SERVICE)
# LibreSSL
set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")
+1 -1
Vendored Submodule
+1
Submodule externals/libzip added at bd7a8103e9.
Vendored Submodule
+1
Submodule externals/zlib added at cacf7f1d4e.
+21 -12
View File
@@ -341,15 +341,24 @@ Public License instead of this License.
The icons used in this project have the following licenses:
Icon Name | License | Origin/Author
--- | --- | ---
checked.png | Free for non-commercial use
failed.png | Free for non-commercial use
lock.png | CC BY-ND 3.0 | https://icons8.com
plus_folder.png | CC BY-ND 3.0 | https://icons8.com
bad_folder.png | CC BY-ND 3.0 | https://icons8.com
chip.png | CC BY-ND 3.0 | https://icons8.com
folder.png | CC BY-ND 3.0 | https://icons8.com
plus.png (Default, Dark) | CC0 1.0 | Designed by BreadFish64 from the Citra team
plus.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
sd_card.png | CC BY-ND 3.0 | https://icons8.com
Icon Name | License | Origin/Author
--- | --- | ---
checked.png | Free for non-commercial use
failed.png | Free for non-commercial use
lock.png | CC BY-ND 3.0 | https://icons8.com
plus_folder.png (Default, Dark) | CC BY-ND 3.0 | https://icons8.com
bad_folder.png (Default, Dark) | CC BY-ND 3.0 | https://icons8.com
chip.png (Default, Dark) | CC BY-ND 3.0 | https://icons8.com
folder.png (Default, Dark) | CC BY-ND 3.0 | https://icons8.com
plus.png (Default, Dark) | CC0 1.0 | Designed by BreadFish64 from the Citra team
sd_card.png (Default, Dark) | CC BY-ND 3.0 | https://icons8.com
plus_folder.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
bad_folder.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
chip.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
folder.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
plus.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
sd_card.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
Note:
Some icons are different in different themes, and they are separately listed
only when they have different licenses/origins.
+10
View File
@@ -107,6 +107,11 @@ Stream::State AudioRenderer::GetStreamState() const {
return stream->GetState();
}
static constexpr u32 VersionFromRevision(u32_le rev) {
// "REV7" -> 7
return ((rev >> 24) & 0xff) - 0x30;
}
std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) {
// Copy UpdateDataHeader struct
UpdateDataHeader config{};
@@ -166,6 +171,11 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_
// Copy output header
UpdateDataHeader response_data{worker_params};
std::vector<u8> output_params(response_data.total_size);
const auto audren_revision = VersionFromRevision(config.revision);
if (audren_revision >= 5) {
response_data.frame_count = 0x10;
response_data.total_size += 0x10;
}
std::memcpy(output_params.data(), &response_data, sizeof(UpdateDataHeader));
// Copy output memory pool entries
+14 -11
View File
@@ -194,21 +194,24 @@ struct UpdateDataHeader {
mixes_size = 0x0;
sinks_size = config.sink_count * 0x20;
performance_manager_size = 0x10;
frame_count = 0;
total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size + voices_size +
effects_size + sinks_size + performance_manager_size;
}
u32_le revision;
u32_le behavior_size;
u32_le memory_pools_size;
u32_le voices_size;
u32_le voice_resource_size;
u32_le effects_size;
u32_le mixes_size;
u32_le sinks_size;
u32_le performance_manager_size;
INSERT_PADDING_WORDS(6);
u32_le total_size;
u32_le revision{};
u32_le behavior_size{};
u32_le memory_pools_size{};
u32_le voices_size{};
u32_le voice_resource_size{};
u32_le effects_size{};
u32_le mixes_size{};
u32_le sinks_size{};
u32_le performance_manager_size{};
INSERT_PADDING_WORDS(1);
u32_le frame_count{};
INSERT_PADDING_WORDS(4);
u32_le total_size{};
};
static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size");
+21
View File
@@ -10,13 +10,28 @@ if (DEFINED ENV{CI})
elseif(DEFINED ENV{APPVEYOR})
set(BUILD_REPOSITORY $ENV{APPVEYOR_REPO_NAME})
set(BUILD_TAG $ENV{APPVEYOR_REPO_TAG_NAME})
elseif(DEFINED ENV{AZURE})
set(BUILD_REPOSITORY $ENV{AZURE_REPO_NAME})
set(BUILD_TAG $ENV{AZURE_REPO_TAG})
endif()
endif()
if (DEFINED ENV{TITLEBARFORMATIDLE})
set(TITLE_BAR_FORMAT_IDLE $ENV{TITLEBARFORMATIDLE})
endif ()
if (DEFINED ENV{TITLEBARFORMATRUNNING})
set(TITLE_BAR_FORMAT_RUNNING $ENV{TITLEBARFORMATRUNNING})
endif ()
if (DEFINED ENV{DISPLAYVERSION})
set(DISPLAY_VERSION $ENV{DISPLAYVERSION})
endif ()
add_custom_command(OUTPUT scm_rev.cpp
COMMAND ${CMAKE_COMMAND}
-DSRC_DIR="${CMAKE_SOURCE_DIR}"
-DBUILD_REPOSITORY="${BUILD_REPOSITORY}"
-DTITLE_BAR_FORMAT_IDLE="${TITLE_BAR_FORMAT_IDLE}"
-DTITLE_BAR_FORMAT_RUNNING="${TITLE_BAR_FORMAT_RUNNING}"
-DBUILD_TAG="${BUILD_TAG}"
-DBUILD_ID="${DISPLAY_VERSION}"
-P "${CMAKE_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake"
DEPENDS
# WARNING! It was too much work to try and make a common location for this list,
@@ -57,9 +72,15 @@ add_custom_command(OUTPUT scm_rev.cpp
"${VIDEO_CORE}/shader/decode/video.cpp"
"${VIDEO_CORE}/shader/decode/warp.cpp"
"${VIDEO_CORE}/shader/decode/xmad.cpp"
"${VIDEO_CORE}/shader/ast.cpp"
"${VIDEO_CORE}/shader/ast.h"
"${VIDEO_CORE}/shader/control_flow.cpp"
"${VIDEO_CORE}/shader/control_flow.h"
"${VIDEO_CORE}/shader/compiler_settings.cpp"
"${VIDEO_CORE}/shader/compiler_settings.h"
"${VIDEO_CORE}/shader/decode.cpp"
"${VIDEO_CORE}/shader/expr.cpp"
"${VIDEO_CORE}/shader/expr.h"
"${VIDEO_CORE}/shader/node.h"
"${VIDEO_CORE}/shader/node_helper.cpp"
"${VIDEO_CORE}/shader/node_helper.h"
+10
View File
@@ -51,7 +51,17 @@ public:
using reference = T&;
using const_reference = const T&;
using propagate_on_container_copy_assignment = std::true_type;
using propagate_on_container_move_assignment = std::true_type;
using propagate_on_container_swap = std::true_type;
using is_always_equal = std::true_type;
public:
constexpr AlignmentAllocator() noexcept = default;
template <typename T2>
constexpr AlignmentAllocator(const AlignmentAllocator<T2, Align>&) noexcept {}
pointer address(reference r) noexcept {
return std::addressof(r);
}
+6 -10
View File
@@ -28,18 +28,14 @@ __declspec(noinline, noreturn)
}
#define ASSERT(_a_) \
do \
if (!(_a_)) { \
assert_noinline_call([] { LOG_CRITICAL(Debug, "Assertion Failed!"); }); \
} \
while (0)
if (!(_a_)) { \
LOG_CRITICAL(Debug, "Assertion Failed!"); \
}
#define ASSERT_MSG(_a_, ...) \
do \
if (!(_a_)) { \
assert_noinline_call([&] { LOG_CRITICAL(Debug, "Assertion Failed!\n" __VA_ARGS__); }); \
} \
while (0)
if (!(_a_)) { \
LOG_CRITICAL(Debug, "Assertion Failed! " __VA_ARGS__); \
}
#define UNREACHABLE() ASSERT_MSG(false, "Unreachable code!")
#define UNREACHABLE_MSG(...) ASSERT_MSG(false, __VA_ARGS__)
+1
View File
@@ -36,6 +36,7 @@
#define LOAD_DIR "load"
#define DUMP_DIR "dump"
#define SHADER_DIR "shader"
#define RESCALING_DIR "rescaling"
#define LOG_DIR "log"
// Filenames
+3 -1
View File
@@ -695,6 +695,7 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) {
paths.emplace(UserPath::LoadDir, user_path + LOAD_DIR DIR_SEP);
paths.emplace(UserPath::DumpDir, user_path + DUMP_DIR DIR_SEP);
paths.emplace(UserPath::ShaderDir, user_path + SHADER_DIR DIR_SEP);
paths.emplace(UserPath::RescalingDir, user_path + RESCALING_DIR DIR_SEP);
paths.emplace(UserPath::SysDataDir, user_path + SYSDATA_DIR DIR_SEP);
paths.emplace(UserPath::KeysDir, user_path + KEYS_DIR DIR_SEP);
// TODO: Put the logs in a better location for each OS
@@ -713,7 +714,6 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) {
case UserPath::RootDir:
user_path = paths[UserPath::RootDir] + DIR_SEP;
break;
case UserPath::UserDir:
user_path = paths[UserPath::RootDir] + DIR_SEP;
paths[UserPath::ConfigDir] = user_path + CONFIG_DIR DIR_SEP;
@@ -721,6 +721,8 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) {
paths[UserPath::SDMCDir] = user_path + SDMC_DIR DIR_SEP;
paths[UserPath::NANDDir] = user_path + NAND_DIR DIR_SEP;
break;
default:
break;
}
}
+1
View File
@@ -33,6 +33,7 @@ enum class UserPath {
LoadDir,
DumpDir,
ShaderDir,
RescalingDir,
SysDataDir,
UserDir,
};
+1
View File
@@ -255,6 +255,7 @@ void DebuggerBackend::Write(const Entry& entry) {
CLS(Input) \
CLS(Network) \
CLS(Loader) \
CLS(CheatEngine) \
CLS(Crypto) \
CLS(WebService)
+1
View File
@@ -117,6 +117,7 @@ enum class Class : ClassType {
Audio_DSP, ///< The HLE implementation of the DSP
Audio_Sink, ///< Emulator audio output backend
Loader, ///< ROM loader
CheatEngine, ///< Memory manipulation and engine VM functions
Crypto, ///< Cryptographic engine/functions
Input, ///< Input emulation
Network, ///< Network emulation
+6
View File
@@ -11,6 +11,9 @@
#define BUILD_DATE "@BUILD_DATE@"
#define BUILD_FULLNAME "@BUILD_FULLNAME@"
#define BUILD_VERSION "@BUILD_VERSION@"
#define BUILD_ID "@BUILD_ID@"
#define TITLE_BAR_FORMAT_IDLE "@TITLE_BAR_FORMAT_IDLE@"
#define TITLE_BAR_FORMAT_RUNNING "@TITLE_BAR_FORMAT_RUNNING@"
#define SHADER_CACHE_VERSION "@SHADER_CACHE_VERSION@"
namespace Common {
@@ -22,6 +25,9 @@ const char g_build_name[] = BUILD_NAME;
const char g_build_date[] = BUILD_DATE;
const char g_build_fullname[] = BUILD_FULLNAME;
const char g_build_version[] = BUILD_VERSION;
const char g_build_id[] = BUILD_ID;
const char g_title_bar_format_idle[] = TITLE_BAR_FORMAT_IDLE;
const char g_title_bar_format_running[] = TITLE_BAR_FORMAT_RUNNING;
const char g_shader_cache_version[] = SHADER_CACHE_VERSION;
} // namespace
+3
View File
@@ -13,6 +13,9 @@ extern const char g_build_name[];
extern const char g_build_date[];
extern const char g_build_fullname[];
extern const char g_build_version[];
extern const char g_build_id[];
extern const char g_title_bar_format_idle[];
extern const char g_title_bar_format_running[];
extern const char g_shader_cache_version[];
} // namespace Common
+25 -2
View File
@@ -1,3 +1,9 @@
if (YUZU_ENABLE_BOXCAT)
set(BCAT_BOXCAT_ADDITIONAL_SOURCES hle/service/bcat/backend/boxcat.cpp hle/service/bcat/backend/boxcat.h)
else()
set(BCAT_BOXCAT_ADDITIONAL_SOURCES)
endif()
add_library(core STATIC
arm/arm_interface.h
arm/arm_interface.cpp
@@ -33,8 +39,6 @@ add_library(core STATIC
file_sys/bis_factory.h
file_sys/card_image.cpp
file_sys/card_image.h
file_sys/cheat_engine.cpp
file_sys/cheat_engine.h
file_sys/content_archive.cpp
file_sys/content_archive.h
file_sys/control_metadata.cpp
@@ -84,6 +88,8 @@ add_library(core STATIC
file_sys/vfs_concat.h
file_sys/vfs_layered.cpp
file_sys/vfs_layered.h
file_sys/vfs_libzip.cpp
file_sys/vfs_libzip.h
file_sys/vfs_offset.cpp
file_sys/vfs_offset.h
file_sys/vfs_real.cpp
@@ -243,6 +249,9 @@ add_library(core STATIC
hle/service/audio/errors.h
hle/service/audio/hwopus.cpp
hle/service/audio/hwopus.h
hle/service/bcat/backend/backend.cpp
hle/service/bcat/backend/backend.h
${BCAT_BOXCAT_ADDITIONAL_SOURCES}
hle/service/bcat/bcat.cpp
hle/service/bcat/bcat.h
hle/service/bcat/module.cpp
@@ -477,6 +486,11 @@ add_library(core STATIC
loader/nsp.h
loader/xci.cpp
loader/xci.h
memory/cheat_engine.cpp
memory/cheat_engine.h
memory/dmnt_cheat_types.h
memory/dmnt_cheat_vm.cpp
memory/dmnt_cheat_vm.h
memory.cpp
memory.h
memory_setup.h
@@ -496,6 +510,15 @@ 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 json-headers mbedtls opus unicorn open_source_archives)
if (YUZU_ENABLE_BOXCAT)
get_directory_property(OPENSSL_LIBS
DIRECTORY ${PROJECT_SOURCE_DIR}/externals/libressl
DEFINITION OPENSSL_LIBS)
target_compile_definitions(core PRIVATE -DCPPHTTPLIB_OPENSSL_SUPPORT -DYUZU_ENABLE_BOXCAT)
target_link_libraries(core PRIVATE httplib json-headers ${OPENSSL_LIBS} zip)
endif()
if (ENABLE_WEB_SERVICE)
target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE)
target_link_libraries(core PRIVATE web_service)
+97 -19
View File
@@ -14,8 +14,14 @@
#include "core/core_cpu.h"
#include "core/core_timing.h"
#include "core/cpu_core_manager.h"
#include "core/file_sys/bis_factory.h"
#include "core/file_sys/card_image.h"
#include "core/file_sys/mode.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs_factory.h"
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/sdmc_factory.h"
#include "core/file_sys/vfs_concat.h"
#include "core/file_sys/vfs_real.h"
#include "core/gdbstub/gdbstub.h"
@@ -27,17 +33,17 @@
#include "core/hle/kernel/thread.h"
#include "core/hle/service/am/applets/applets.h"
#include "core/hle/service/apm/controller.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/glue/manager.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
#include "core/loader/loader.h"
#include "core/memory/cheat_engine.h"
#include "core/perf_stats.h"
#include "core/reporter.h"
#include "core/settings.h"
#include "core/telemetry_session.h"
#include "core/tools/freezer.h"
#include "file_sys/cheat_engine.h"
#include "file_sys/patch_manager.h"
#include "video_core/debug_utils/debug_utils.h"
#include "video_core/renderer_base.h"
#include "video_core/video_core.h"
@@ -104,7 +110,9 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
return vfs->OpenFile(path, FileSys::Mode::Read);
}
struct System::Impl {
explicit Impl(System& system) : kernel{system}, cpu_core_manager{system}, reporter{system} {}
explicit Impl(System& system)
: kernel{system}, fs_controller{system}, cpu_core_manager{system},
applet_manager{system}, reporter{system} {}
Cpu& CurrentCpuCore() {
return cpu_core_manager.GetCurrentCore();
@@ -156,13 +164,10 @@ struct System::Impl {
gpu_core = VideoCore::CreateGPU(system);
is_powered_on = true;
exit_lock = false;
LOG_DEBUG(Core, "Initialized OK");
// Reset counters and set time origin to current frame
GetAndResetPerfStats();
perf_stats.BeginSystemFrame();
return ResultStatus::Success;
}
@@ -201,10 +206,34 @@ struct System::Impl {
gpu_core->Start();
cpu_core_manager.StartThreads();
// Initialize cheat engine
if (cheat_engine) {
cheat_engine->Initialize();
}
// All threads are started, begin main process execution, now that we're in the clear.
main_process->Run(load_parameters->main_thread_priority,
load_parameters->main_thread_stack_size);
if (Settings::values.gamecard_inserted) {
if (Settings::values.gamecard_current_game) {
fs_controller.SetGameCard(GetGameFileFromPath(virtual_filesystem, filepath));
} else if (!Settings::values.gamecard_path.empty()) {
fs_controller.SetGameCard(
GetGameFileFromPath(virtual_filesystem, Settings::values.gamecard_path));
}
}
u64 title_id{0};
if (app_loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) {
LOG_ERROR(Core, "Failed to find title id for ROM (Error {})",
static_cast<u32>(load_result));
}
perf_stats = std::make_unique<PerfStats>(title_id);
// Reset counters and set time origin to current frame
GetAndResetPerfStats();
perf_stats->BeginSystemFrame();
status = ResultStatus::Success;
return status;
}
@@ -218,8 +247,13 @@ struct System::Impl {
perf_results.game_fps);
telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime",
perf_results.frametime * 1000.0);
telemetry_session->AddField(Telemetry::FieldType::Performance, "Mean_Frametime_MS",
perf_stats->GetMeanFrametime());
is_powered_on = false;
exit_lock = false;
gpu_core->WaitIdle();
// Shutdown emulation session
renderer.reset();
@@ -228,6 +262,7 @@ struct System::Impl {
service_manager.reset();
cheat_engine.reset();
telemetry_session.reset();
perf_stats.reset();
gpu_core.reset();
// Close all CPU/threading state
@@ -285,7 +320,7 @@ struct System::Impl {
}
PerfStatsResults GetAndResetPerfStats() {
return perf_stats.GetAndResetStats(core_timing.GetGlobalTimeUs());
return perf_stats->GetAndResetStats(core_timing.GetGlobalTimeUs());
}
Timing::CoreTiming core_timing;
@@ -294,6 +329,7 @@ struct System::Impl {
FileSys::VirtualFilesystem virtual_filesystem;
/// ContentProviderUnion instance
std::unique_ptr<FileSys::ContentProviderUnion> content_provider;
Service::FileSystem::FileSystemController fs_controller;
/// AppLoader used to load the current executing application
std::unique_ptr<Loader::AppLoader> app_loader;
std::unique_ptr<VideoCore::RendererBase> renderer;
@@ -302,9 +338,11 @@ struct System::Impl {
std::unique_ptr<Core::Hardware::InterruptManager> interrupt_manager;
CpuCoreManager cpu_core_manager;
bool is_powered_on = false;
bool exit_lock = false;
std::unique_ptr<FileSys::CheatEngine> cheat_engine;
std::unique_ptr<Memory::CheatEngine> cheat_engine;
std::unique_ptr<Tools::Freezer> memory_freezer;
std::array<u8, 0x20> build_id{};
/// Frontend applets
Service::AM::Applets::AppletManager applet_manager;
@@ -326,7 +364,7 @@ struct System::Impl {
ResultStatus status = ResultStatus::Success;
std::string status_details = "";
Core::PerfStats perf_stats;
std::unique_ptr<Core::PerfStats> perf_stats;
Core::FrameLimiter frame_limiter;
};
@@ -365,6 +403,12 @@ void System::PrepareReschedule() {
CurrentCpuCore().PrepareReschedule();
}
void System::PrepareReschedule(const u32 core_index) {
if (core_index < GlobalScheduler().CpuCoresCount()) {
CpuCore(core_index).PrepareReschedule();
}
}
PerfStatsResults System::GetAndResetPerfStats() {
return impl->GetAndResetPerfStats();
}
@@ -405,6 +449,16 @@ const Kernel::Scheduler& System::Scheduler(std::size_t core_index) const {
return CpuCore(core_index).Scheduler();
}
/// Gets the global scheduler
Kernel::GlobalScheduler& System::GlobalScheduler() {
return impl->kernel.GlobalScheduler();
}
/// Gets the global scheduler
const Kernel::GlobalScheduler& System::GlobalScheduler() const {
return impl->kernel.GlobalScheduler();
}
Kernel::Process* System::CurrentProcess() {
return impl->kernel.CurrentProcess();
}
@@ -479,11 +533,11 @@ const Timing::CoreTiming& System::CoreTiming() const {
}
Core::PerfStats& System::GetPerfStats() {
return impl->perf_stats;
return *impl->perf_stats;
}
const Core::PerfStats& System::GetPerfStats() const {
return impl->perf_stats;
return *impl->perf_stats;
}
Core::FrameLimiter& System::FrameLimiter() {
@@ -518,13 +572,6 @@ Tegra::DebugContext* System::GetGPUDebugContext() const {
return impl->debug_context.get();
}
void System::RegisterCheatList(const std::vector<FileSys::CheatList>& list,
const std::string& build_id, VAddr code_region_start,
VAddr code_region_end) {
impl->cheat_engine = std::make_unique<FileSys::CheatEngine>(*this, list, build_id,
code_region_start, code_region_end);
}
void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) {
impl->virtual_filesystem = std::move(vfs);
}
@@ -533,6 +580,13 @@ std::shared_ptr<FileSys::VfsFilesystem> System::GetFilesystem() const {
return impl->virtual_filesystem;
}
void System::RegisterCheatList(const std::vector<Memory::CheatEntry>& list,
const std::array<u8, 32>& build_id, VAddr main_region_begin,
u64 main_region_size) {
impl->cheat_engine = std::make_unique<Memory::CheatEngine>(*this, list, build_id);
impl->cheat_engine->SetMainMemoryParameters(main_region_begin, main_region_size);
}
void System::SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set) {
impl->applet_manager.SetAppletFrontendSet(std::move(set));
}
@@ -561,6 +615,14 @@ const FileSys::ContentProvider& System::GetContentProvider() const {
return *impl->content_provider;
}
Service::FileSystem::FileSystemController& System::GetFileSystemController() {
return impl->fs_controller;
}
const Service::FileSystem::FileSystemController& System::GetFileSystemController() const {
return impl->fs_controller;
}
void System::RegisterContentProvider(FileSys::ContentProviderUnionSlot slot,
FileSys::ContentProvider* provider) {
impl->content_provider->SetSlot(slot, provider);
@@ -590,6 +652,22 @@ const Service::APM::Controller& System::GetAPMController() const {
return impl->apm_controller;
}
void System::SetExitLock(bool locked) {
impl->exit_lock = locked;
}
bool System::GetExitLock() const {
return impl->exit_lock;
}
void System::SetCurrentProcessBuildID(const CurrentBuildProcessID& id) {
impl->build_id = id;
}
const System::CurrentBuildProcessID& System::GetCurrentProcessBuildID() const {
return impl->build_id;
}
System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
return impl->Init(*this, emu_window);
}
+35 -8
View File
@@ -8,7 +8,6 @@
#include <memory>
#include <string>
#include <map>
#include "common/common_types.h"
#include "core/file_sys/vfs_types.h"
#include "core/hle/kernel/object.h"
@@ -18,7 +17,6 @@ class EmuWindow;
} // namespace Core::Frontend
namespace FileSys {
class CheatList;
class ContentProvider;
class ContentProviderUnion;
enum class ContentProviderUnionSlot;
@@ -26,6 +24,7 @@ class VfsFilesystem;
} // namespace FileSys
namespace Kernel {
class GlobalScheduler;
class KernelCore;
class Process;
class Scheduler;
@@ -36,6 +35,10 @@ class AppLoader;
enum class ResultStatus : u16;
} // namespace Loader
namespace Memory {
struct CheatEntry;
} // namespace Memory
namespace Service {
namespace AM::Applets {
@@ -47,6 +50,10 @@ namespace APM {
class Controller;
}
namespace FileSystem {
class FileSystemController;
} // namespace FileSystem
namespace Glue {
class ARPManager;
}
@@ -91,6 +98,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
class System {
public:
using CurrentBuildProcessID = std::array<u8, 0x20>;
System(const System&) = delete;
System& operator=(const System&) = delete;
@@ -172,6 +181,9 @@ public:
/// Prepare the core emulation for a reschedule
void PrepareReschedule();
/// Prepare the core emulation for a reschedule
void PrepareReschedule(u32 core_index);
/// Gets and resets core performance statistics
PerfStatsResults GetAndResetPerfStats();
@@ -226,6 +238,12 @@ public:
/// Gets the scheduler for the CPU core with the specified index
const Kernel::Scheduler& Scheduler(std::size_t core_index) const;
/// Gets the global scheduler
Kernel::GlobalScheduler& GlobalScheduler();
/// Gets the global scheduler
const Kernel::GlobalScheduler& GlobalScheduler() const;
/// Provides a pointer to the current process
Kernel::Process* CurrentProcess();
@@ -282,8 +300,9 @@ public:
std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const;
void RegisterCheatList(const std::vector<FileSys::CheatList>& list, const std::string& build_id,
VAddr code_region_start, VAddr code_region_end);
void RegisterCheatList(const std::vector<Memory::CheatEntry>& list,
const std::array<u8, 0x20>& build_id, VAddr main_region_begin,
u64 main_region_size);
void SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set);
@@ -299,6 +318,10 @@ public:
const FileSys::ContentProvider& GetContentProvider() const;
Service::FileSystem::FileSystemController& GetFileSystemController();
const Service::FileSystem::FileSystemController& GetFileSystemController() const;
void RegisterContentProvider(FileSys::ContentProviderUnionSlot slot,
FileSys::ContentProvider* provider);
@@ -314,6 +337,14 @@ public:
const Service::APM::Controller& GetAPMController() const;
void SetExitLock(bool locked);
bool GetExitLock() const;
void SetCurrentProcessBuildID(const CurrentBuildProcessID& id);
const CurrentBuildProcessID& GetCurrentProcessBuildID() const;
private:
System();
@@ -337,8 +368,4 @@ private:
static System s_instance;
};
inline Kernel::Process* CurrentProcess() {
return System::GetInstance().CurrentProcess();
}
} // namespace Core
+9 -11
View File
@@ -52,7 +52,8 @@ bool CpuBarrier::Rendezvous() {
Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_barrier,
std::size_t core_index)
: cpu_barrier{cpu_barrier}, core_timing{system.CoreTiming()}, core_index{core_index} {
: cpu_barrier{cpu_barrier}, global_scheduler{system.GlobalScheduler()},
core_timing{system.CoreTiming()}, core_index{core_index} {
#ifdef ARCHITECTURE_x86_64
arm_interface = std::make_unique<ARM_Dynarmic>(system, exclusive_monitor, core_index);
#else
@@ -60,7 +61,7 @@ Cpu::Cpu(System& system, ExclusiveMonitor& exclusive_monitor, CpuBarrier& cpu_ba
LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
#endif
scheduler = std::make_unique<Kernel::Scheduler>(system, *arm_interface);
scheduler = std::make_unique<Kernel::Scheduler>(system, *arm_interface, core_index);
}
Cpu::~Cpu() = default;
@@ -81,6 +82,8 @@ void Cpu::RunLoop(bool tight_loop) {
return;
}
Reschedule();
// If we don't have a currently active thread then don't execute instructions,
// instead advance to the next event and try to yield to the next thread
if (Kernel::GetCurrentThread() == nullptr) {
@@ -92,7 +95,6 @@ void Cpu::RunLoop(bool tight_loop) {
core_timing.Advance();
}
PrepareReschedule();
} else {
if (IsMainCore()) {
core_timing.Advance();
@@ -114,18 +116,14 @@ void Cpu::SingleStep() {
void Cpu::PrepareReschedule() {
arm_interface->PrepareReschedule();
reschedule_pending = true;
}
void Cpu::Reschedule() {
if (!reschedule_pending) {
return;
}
reschedule_pending = false;
// Lock the global kernel mutex when we manipulate the HLE state
std::lock_guard lock{HLE::g_hle_lock};
scheduler->Reschedule();
std::lock_guard lock(HLE::g_hle_lock);
global_scheduler.SelectThread(core_index);
scheduler->TryDoContextSwitch();
}
} // namespace Core
+3 -1
View File
@@ -12,8 +12,9 @@
#include "common/common_types.h"
namespace Kernel {
class GlobalScheduler;
class Scheduler;
}
} // namespace Kernel
namespace Core {
class System;
@@ -90,6 +91,7 @@ private:
std::unique_ptr<ARM_Interface> arm_interface;
CpuBarrier& cpu_barrier;
Kernel::GlobalScheduler& global_scheduler;
std::unique_ptr<Kernel::Scheduler> scheduler;
Timing::CoreTiming& core_timing;
+1 -1
View File
@@ -423,7 +423,7 @@ static std::optional<u64> FindTicketOffset(const std::array<u8, size>& data) {
std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket,
const RSAKeyPair<2048>& key) {
const auto issuer = ticket.GetData().issuer;
if (issuer == std::array<u8, 0x40>{})
if (IsAllZeroArray(issuer))
return {};
if (issuer[0] != 'R' || issuer[1] != 'o' || issuer[2] != 'o' || issuer[3] != 't') {
LOG_INFO(Crypto, "Attempting to parse ticket with non-standard certificate authority.");
@@ -480,6 +480,10 @@ void PartitionDataManager::DecryptProdInfo(std::array<u8, 0x20> bis_key) {
prodinfo_decrypted = std::make_shared<XTSEncryptionLayer>(prodinfo, bis_key);
}
FileSys::VirtualFile PartitionDataManager::GetDecryptedProdInfo() const {
return prodinfo_decrypted;
}
std::array<u8, 576> PartitionDataManager::GetETicketExtendedKek() const {
std::array<u8, 0x240> out{};
if (prodinfo_decrypted != nullptr)
+1
View File
@@ -84,6 +84,7 @@ public:
bool HasProdInfo() const;
FileSys::VirtualFile GetProdInfoRaw() const;
void DecryptProdInfo(std::array<u8, 0x20> bis_key);
FileSys::VirtualFile GetDecryptedProdInfo() const;
std::array<u8, 0x240> GetETicketExtendedKek() const;
private:
+104 -2
View File
@@ -3,8 +3,12 @@
// Refer to the license.txt file included.
#include <fmt/format.h>
#include "common/file_util.h"
#include "core/core.h"
#include "core/file_sys/bis_factory.h"
#include "core/file_sys/mode.h"
#include "core/file_sys/registered_cache.h"
#include "core/settings.h"
namespace FileSys {
@@ -14,10 +18,22 @@ BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_, VirtualDir
sysnand_cache(std::make_unique<RegisteredCache>(
GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))),
usrnand_cache(std::make_unique<RegisteredCache>(
GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))) {}
GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))),
sysnand_placeholder(std::make_unique<PlaceholderCache>(
GetOrCreateDirectoryRelative(nand_root, "/system/Contents/placehld"))),
usrnand_placeholder(std::make_unique<PlaceholderCache>(
GetOrCreateDirectoryRelative(nand_root, "/user/Contents/placehld"))) {}
BISFactory::~BISFactory() = default;
VirtualDir BISFactory::GetSystemNANDContentDirectory() const {
return GetOrCreateDirectoryRelative(nand_root, "/system/Contents");
}
VirtualDir BISFactory::GetUserNANDContentDirectory() const {
return GetOrCreateDirectoryRelative(nand_root, "/user/Contents");
}
RegisteredCache* BISFactory::GetSystemNANDContents() const {
return sysnand_cache.get();
}
@@ -26,9 +42,17 @@ RegisteredCache* BISFactory::GetUserNANDContents() const {
return usrnand_cache.get();
}
PlaceholderCache* BISFactory::GetSystemNANDPlaceholder() const {
return sysnand_placeholder.get();
}
PlaceholderCache* BISFactory::GetUserNANDPlaceholder() const {
return usrnand_placeholder.get();
}
VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const {
// LayeredFS doesn't work on updates and title id-less homebrew
if (title_id == 0 || (title_id & 0x800) > 0)
if (title_id == 0 || (title_id & 0xFFF) == 0x800)
return nullptr;
return GetOrCreateDirectoryRelative(load_root, fmt::format("/{:016X}", title_id));
}
@@ -39,4 +63,82 @@ VirtualDir BISFactory::GetModificationDumpRoot(u64 title_id) const {
return GetOrCreateDirectoryRelative(dump_root, fmt::format("/{:016X}", title_id));
}
VirtualDir BISFactory::OpenPartition(BisPartitionId id) const {
switch (id) {
case BisPartitionId::CalibrationFile:
return GetOrCreateDirectoryRelative(nand_root, "/prodinfof");
case BisPartitionId::SafeMode:
return GetOrCreateDirectoryRelative(nand_root, "/safe");
case BisPartitionId::System:
return GetOrCreateDirectoryRelative(nand_root, "/system");
case BisPartitionId::User:
return GetOrCreateDirectoryRelative(nand_root, "/user");
default:
return nullptr;
}
}
VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id) const {
Core::Crypto::KeyManager keys;
Core::Crypto::PartitionDataManager pdm{
Core::System::GetInstance().GetFilesystem()->OpenDirectory(
FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), Mode::Read)};
keys.PopulateFromPartitionData(pdm);
switch (id) {
case BisPartitionId::CalibrationBinary:
return pdm.GetDecryptedProdInfo();
case BisPartitionId::BootConfigAndPackage2Part1:
case BisPartitionId::BootConfigAndPackage2Part2:
case BisPartitionId::BootConfigAndPackage2Part3:
case BisPartitionId::BootConfigAndPackage2Part4:
case BisPartitionId::BootConfigAndPackage2Part5:
case BisPartitionId::BootConfigAndPackage2Part6: {
const auto new_id = static_cast<u8>(id) -
static_cast<u8>(BisPartitionId::BootConfigAndPackage2Part1) +
static_cast<u8>(Core::Crypto::Package2Type::NormalMain);
return pdm.GetPackage2Raw(static_cast<Core::Crypto::Package2Type>(new_id));
}
default:
return nullptr;
}
}
VirtualDir BISFactory::GetImageDirectory() const {
return GetOrCreateDirectoryRelative(nand_root, "/user/Album");
}
u64 BISFactory::GetSystemNANDFreeSpace() const {
const auto sys_dir = GetOrCreateDirectoryRelative(nand_root, "/system");
if (sys_dir == nullptr)
return 0;
return GetSystemNANDTotalSpace() - sys_dir->GetSize();
}
u64 BISFactory::GetSystemNANDTotalSpace() const {
return static_cast<u64>(Settings::values.nand_system_size);
}
u64 BISFactory::GetUserNANDFreeSpace() const {
const auto usr_dir = GetOrCreateDirectoryRelative(nand_root, "/user");
if (usr_dir == nullptr)
return 0;
return GetUserNANDTotalSpace() - usr_dir->GetSize();
}
u64 BISFactory::GetUserNANDTotalSpace() const {
return static_cast<u64>(Settings::values.nand_user_size);
}
u64 BISFactory::GetFullNANDTotalSpace() const {
return static_cast<u64>(Settings::values.nand_total_size);
}
VirtualDir BISFactory::GetBCATDirectory(u64 title_id) const {
return GetOrCreateDirectoryRelative(nand_root,
fmt::format("/system/save/bcat/{:016X}", title_id));
}
} // namespace FileSys
+40
View File
@@ -10,7 +10,25 @@
namespace FileSys {
enum class BisPartitionId : u32 {
UserDataRoot = 20,
CalibrationBinary = 27,
CalibrationFile = 28,
BootConfigAndPackage2Part1 = 21,
BootConfigAndPackage2Part2 = 22,
BootConfigAndPackage2Part3 = 23,
BootConfigAndPackage2Part4 = 24,
BootConfigAndPackage2Part5 = 25,
BootConfigAndPackage2Part6 = 26,
SafeMode = 29,
System = 31,
SystemProperEncryption = 32,
SystemProperPartition = 33,
User = 30,
};
class RegisteredCache;
class PlaceholderCache;
/// File system interface to the Built-In Storage
/// This is currently missing accessors to BIS partitions, but seemed like a good place for the NAND
@@ -20,12 +38,31 @@ public:
explicit BISFactory(VirtualDir nand_root, VirtualDir load_root, VirtualDir dump_root);
~BISFactory();
VirtualDir GetSystemNANDContentDirectory() const;
VirtualDir GetUserNANDContentDirectory() const;
RegisteredCache* GetSystemNANDContents() const;
RegisteredCache* GetUserNANDContents() const;
PlaceholderCache* GetSystemNANDPlaceholder() const;
PlaceholderCache* GetUserNANDPlaceholder() const;
VirtualDir GetModificationLoadRoot(u64 title_id) const;
VirtualDir GetModificationDumpRoot(u64 title_id) const;
VirtualDir OpenPartition(BisPartitionId id) const;
VirtualFile OpenPartitionStorage(BisPartitionId id) const;
VirtualDir GetImageDirectory() const;
u64 GetSystemNANDFreeSpace() const;
u64 GetSystemNANDTotalSpace() const;
u64 GetUserNANDFreeSpace() const;
u64 GetUserNANDTotalSpace() const;
u64 GetFullNANDTotalSpace() const;
VirtualDir GetBCATDirectory(u64 title_id) const;
private:
VirtualDir nand_root;
VirtualDir load_root;
@@ -33,6 +70,9 @@ private:
std::unique_ptr<RegisteredCache> sysnand_cache;
std::unique_ptr<RegisteredCache> usrnand_cache;
std::unique_ptr<PlaceholderCache> sysnand_placeholder;
std::unique_ptr<PlaceholderCache> usrnand_placeholder;
};
} // namespace FileSys
+24
View File
@@ -12,12 +12,16 @@
#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/romfs.h"
#include "core/file_sys/submission_package.h"
#include "core/file_sys/vfs_concat.h"
#include "core/file_sys/vfs_offset.h"
#include "core/file_sys/vfs_vector.h"
#include "core/loader/loader.h"
namespace FileSys {
constexpr u64 GAMECARD_CERTIFICATE_OFFSET = 0x7000;
constexpr std::array partition_names{
"update",
"normal",
@@ -175,6 +179,26 @@ VirtualDir XCI::GetParentDirectory() const {
return file->GetContainingDirectory();
}
VirtualDir XCI::ConcatenatedPseudoDirectory() {
const auto out = std::make_shared<VectorVfsDirectory>();
for (const auto& part_id : {XCIPartition::Normal, XCIPartition::Logo, XCIPartition::Secure}) {
const auto& part = GetPartition(part_id);
if (part == nullptr)
continue;
for (const auto& file : part->GetFiles())
out->AddFile(file);
}
return out;
}
std::array<u8, 0x200> XCI::GetCertificate() const {
std::array<u8, 0x200> out;
file->Read(out.data(), out.size(), GAMECARD_CERTIFICATE_OFFSET);
return out;
}
Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
const auto partition_index = static_cast<std::size_t>(part);
const auto& partition = partitions[partition_index];
+9
View File
@@ -91,6 +91,8 @@ public:
VirtualDir GetLogoPartition() const;
u64 GetProgramTitleID() const;
u32 GetSystemUpdateVersion();
u64 GetSystemUpdateTitleID() const;
bool HasProgramNCA() const;
VirtualFile GetProgramNCAFile() const;
@@ -106,6 +108,11 @@ public:
VirtualDir GetParentDirectory() const override;
// Creates a directory that contains all the NCAs in the gamecard
VirtualDir ConcatenatedPseudoDirectory();
std::array<u8, 0x200> GetCertificate() const;
private:
Loader::ResultStatus AddNCAFromPartition(XCIPartition part);
@@ -120,6 +127,8 @@ private:
std::shared_ptr<NCA> program;
std::vector<std::shared_ptr<NCA>> ncas;
u64 update_normal_partition_end;
Core::Crypto::KeyManager keys;
};
} // namespace FileSys
-492
View File
@@ -1,492 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <locale>
#include "common/hex_util.h"
#include "common/microprofile.h"
#include "common/swap.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
#include "core/file_sys/cheat_engine.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/hid/controllers/npad.h"
#include "core/hle/service/hid/hid.h"
#include "core/hle/service/sm/sm.h"
namespace FileSys {
constexpr s64 CHEAT_ENGINE_TICKS = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 60);
constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF;
u64 Cheat::Address() const {
u64 out;
std::memcpy(&out, raw.data(), sizeof(u64));
return Common::swap64(out) & 0xFFFFFFFFFF;
}
u64 Cheat::ValueWidth(u64 offset) const {
return Value(offset, width);
}
u64 Cheat::Value(u64 offset, u64 width) const {
u64 out;
std::memcpy(&out, raw.data() + offset, sizeof(u64));
out = Common::swap64(out);
if (width == 8)
return out;
return out & ((1ull << (width * CHAR_BIT)) - 1);
}
u32 Cheat::KeypadValue() const {
u32 out;
std::memcpy(&out, raw.data(), sizeof(u32));
return Common::swap32(out) & 0x0FFFFFFF;
}
void CheatList::SetMemoryParameters(VAddr main_begin, VAddr heap_begin, VAddr main_end,
VAddr heap_end, MemoryWriter writer, MemoryReader reader) {
this->main_region_begin = main_begin;
this->main_region_end = main_end;
this->heap_region_begin = heap_begin;
this->heap_region_end = heap_end;
this->writer = writer;
this->reader = reader;
}
MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70));
void CheatList::Execute() {
MICROPROFILE_SCOPE(Cheat_Engine);
std::fill(scratch.begin(), scratch.end(), 0);
in_standard = false;
for (std::size_t i = 0; i < master_list.size(); ++i) {
LOG_DEBUG(Common_Filesystem, "Executing block #{:08X} ({})", i, master_list[i].first);
current_block = i;
ExecuteBlock(master_list[i].second);
}
in_standard = true;
for (std::size_t i = 0; i < standard_list.size(); ++i) {
LOG_DEBUG(Common_Filesystem, "Executing block #{:08X} ({})", i, standard_list[i].first);
current_block = i;
ExecuteBlock(standard_list[i].second);
}
}
CheatList::CheatList(const Core::System& system_, ProgramSegment master, ProgramSegment standard)
: master_list{std::move(master)}, standard_list{std::move(standard)}, system{&system_} {}
bool CheatList::EvaluateConditional(const Cheat& cheat) const {
using ComparisonFunction = bool (*)(u64, u64);
constexpr std::array<ComparisonFunction, 6> comparison_functions{
[](u64 a, u64 b) { return a > b; }, [](u64 a, u64 b) { return a >= b; },
[](u64 a, u64 b) { return a < b; }, [](u64 a, u64 b) { return a <= b; },
[](u64 a, u64 b) { return a == b; }, [](u64 a, u64 b) { return a != b; },
};
if (cheat.type == CodeType::ConditionalInput) {
const auto applet_resource =
system->ServiceManager().GetService<Service::HID::Hid>("hid")->GetAppletResource();
if (applet_resource == nullptr) {
LOG_WARNING(
Common_Filesystem,
"Attempted to evaluate input conditional, but applet resource is not initialized!");
return false;
}
const auto press_state =
applet_resource
->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)
.GetAndResetPressState();
return ((press_state & cheat.KeypadValue()) & KEYPAD_BITMASK) != 0;
}
ASSERT(cheat.type == CodeType::Conditional);
const auto offset =
cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin;
ASSERT(static_cast<u8>(cheat.comparison_op.Value()) < 6);
auto* function = comparison_functions[static_cast<u8>(cheat.comparison_op.Value())];
const auto addr = cheat.Address() + offset;
return function(reader(cheat.width, SanitizeAddress(addr)), cheat.ValueWidth(8));
}
void CheatList::ProcessBlockPairs(const Block& block) {
block_pairs.clear();
u64 scope = 0;
std::map<u64, u64> pairs;
for (std::size_t i = 0; i < block.size(); ++i) {
const auto& cheat = block[i];
switch (cheat.type) {
case CodeType::Conditional:
case CodeType::ConditionalInput:
pairs.insert_or_assign(scope, i);
++scope;
break;
case CodeType::EndConditional: {
--scope;
const auto idx = pairs.at(scope);
block_pairs.insert_or_assign(idx, i);
break;
}
case CodeType::Loop: {
if (cheat.end_of_loop) {
--scope;
const auto idx = pairs.at(scope);
block_pairs.insert_or_assign(idx, i);
} else {
pairs.insert_or_assign(scope, i);
++scope;
}
break;
}
}
}
}
void CheatList::WriteImmediate(const Cheat& cheat) {
const auto offset =
cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin;
const auto& register_3 = scratch.at(cheat.register_3);
const auto addr = cheat.Address() + offset + register_3;
LOG_DEBUG(Common_Filesystem, "writing value={:016X} to addr={:016X}", addr,
cheat.Value(8, cheat.width));
writer(cheat.width, SanitizeAddress(addr), cheat.ValueWidth(8));
}
void CheatList::BeginConditional(const Cheat& cheat) {
if (EvaluateConditional(cheat)) {
return;
}
const auto iter = block_pairs.find(current_index);
ASSERT(iter != block_pairs.end());
current_index = iter->second - 1;
}
void CheatList::EndConditional(const Cheat& cheat) {
LOG_DEBUG(Common_Filesystem, "Ending conditional block.");
}
void CheatList::Loop(const Cheat& cheat) {
if (cheat.end_of_loop.Value())
ASSERT(!cheat.end_of_loop.Value());
auto& register_3 = scratch.at(cheat.register_3);
const auto iter = block_pairs.find(current_index);
ASSERT(iter != block_pairs.end());
ASSERT(iter->first < iter->second);
const s32 initial_value = static_cast<s32>(cheat.Value(4, sizeof(s32)));
for (s32 i = initial_value; i >= 0; --i) {
register_3 = static_cast<u64>(i);
for (std::size_t c = iter->first + 1; c < iter->second; ++c) {
current_index = c;
ExecuteSingleCheat(
(in_standard ? standard_list : master_list)[current_block].second[c]);
}
}
current_index = iter->second;
}
void CheatList::LoadImmediate(const Cheat& cheat) {
auto& register_3 = scratch.at(cheat.register_3);
LOG_DEBUG(Common_Filesystem, "setting register={:01X} equal to value={:016X}", cheat.register_3,
cheat.Value(4, 8));
register_3 = cheat.Value(4, 8);
}
void CheatList::LoadIndexed(const Cheat& cheat) {
const auto offset =
cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin;
auto& register_3 = scratch.at(cheat.register_3);
const auto addr = (cheat.load_from_register.Value() ? register_3 : offset) + cheat.Address();
LOG_DEBUG(Common_Filesystem, "writing indexed value to register={:01X}, addr={:016X}",
cheat.register_3, addr);
register_3 = reader(cheat.width, SanitizeAddress(addr));
}
void CheatList::StoreIndexed(const Cheat& cheat) {
const auto& register_3 = scratch.at(cheat.register_3);
const auto addr =
register_3 + (cheat.add_additional_register.Value() ? scratch.at(cheat.register_6) : 0);
LOG_DEBUG(Common_Filesystem, "writing value={:016X} to addr={:016X}",
cheat.Value(4, cheat.width), addr);
writer(cheat.width, SanitizeAddress(addr), cheat.ValueWidth(4));
}
void CheatList::RegisterArithmetic(const Cheat& cheat) {
using ArithmeticFunction = u64 (*)(u64, u64);
constexpr std::array<ArithmeticFunction, 5> arithmetic_functions{
[](u64 a, u64 b) { return a + b; }, [](u64 a, u64 b) { return a - b; },
[](u64 a, u64 b) { return a * b; }, [](u64 a, u64 b) { return a << b; },
[](u64 a, u64 b) { return a >> b; },
};
using ArithmeticOverflowCheck = bool (*)(u64, u64);
constexpr std::array<ArithmeticOverflowCheck, 5> arithmetic_overflow_checks{
[](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() - b); }, // a + b
[](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() + b); }, // a - b
[](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() / b); }, // a * b
[](u64 a, u64 b) { return b >= 64 || (a & ~((1ull << (64 - b)) - 1)) != 0; }, // a << b
[](u64 a, u64 b) { return b >= 64 || (a & ((1ull << b) - 1)) != 0; }, // a >> b
};
static_assert(sizeof(arithmetic_functions) == sizeof(arithmetic_overflow_checks),
"Missing or have extra arithmetic overflow checks compared to functions!");
auto& register_3 = scratch.at(cheat.register_3);
ASSERT(static_cast<u8>(cheat.arithmetic_op.Value()) < 5);
auto* function = arithmetic_functions[static_cast<u8>(cheat.arithmetic_op.Value())];
auto* overflow_function =
arithmetic_overflow_checks[static_cast<u8>(cheat.arithmetic_op.Value())];
LOG_DEBUG(Common_Filesystem, "performing arithmetic with register={:01X}, value={:016X}",
cheat.register_3, cheat.ValueWidth(4));
if (overflow_function(register_3, cheat.ValueWidth(4))) {
LOG_WARNING(Common_Filesystem,
"overflow will occur when performing arithmetic operation={:02X} with operands "
"a={:016X}, b={:016X}!",
static_cast<u8>(cheat.arithmetic_op.Value()), register_3, cheat.ValueWidth(4));
}
register_3 = function(register_3, cheat.ValueWidth(4));
}
void CheatList::BeginConditionalInput(const Cheat& cheat) {
if (EvaluateConditional(cheat))
return;
const auto iter = block_pairs.find(current_index);
ASSERT(iter != block_pairs.end());
current_index = iter->second - 1;
}
VAddr CheatList::SanitizeAddress(VAddr in) const {
if ((in < main_region_begin || in >= main_region_end) &&
(in < heap_region_begin || in >= heap_region_end)) {
LOG_ERROR(Common_Filesystem,
"Cheat attempting to access memory at invalid address={:016X}, if this persists, "
"the cheat may be incorrect. However, this may be normal early in execution if "
"the game has not properly set up yet.",
in);
return 0; ///< Invalid addresses will hard crash
}
return in;
}
void CheatList::ExecuteSingleCheat(const Cheat& cheat) {
using CheatOperationFunction = void (CheatList::*)(const Cheat&);
constexpr std::array<CheatOperationFunction, 9> cheat_operation_functions{
&CheatList::WriteImmediate, &CheatList::BeginConditional,
&CheatList::EndConditional, &CheatList::Loop,
&CheatList::LoadImmediate, &CheatList::LoadIndexed,
&CheatList::StoreIndexed, &CheatList::RegisterArithmetic,
&CheatList::BeginConditionalInput,
};
const auto index = static_cast<u8>(cheat.type.Value());
ASSERT(index < sizeof(cheat_operation_functions));
const auto op = cheat_operation_functions[index];
(this->*op)(cheat);
}
void CheatList::ExecuteBlock(const Block& block) {
encountered_loops.clear();
ProcessBlockPairs(block);
for (std::size_t i = 0; i < block.size(); ++i) {
current_index = i;
ExecuteSingleCheat(block[i]);
i = current_index;
}
}
CheatParser::~CheatParser() = default;
CheatList CheatParser::MakeCheatList(const Core::System& system, CheatList::ProgramSegment master,
CheatList::ProgramSegment standard) const {
return {system, std::move(master), std::move(standard)};
}
TextCheatParser::~TextCheatParser() = default;
CheatList TextCheatParser::Parse(const Core::System& system, const std::vector<u8>& data) const {
std::stringstream ss;
ss.write(reinterpret_cast<const char*>(data.data()), data.size());
std::vector<std::string> lines;
std::string stream_line;
while (std::getline(ss, stream_line)) {
// Remove a trailing \r
if (!stream_line.empty() && stream_line.back() == '\r')
stream_line.pop_back();
lines.push_back(std::move(stream_line));
}
CheatList::ProgramSegment master_list;
CheatList::ProgramSegment standard_list;
for (std::size_t i = 0; i < lines.size(); ++i) {
auto line = lines[i];
if (!line.empty() && (line[0] == '[' || line[0] == '{')) {
const auto master = line[0] == '{';
const auto begin = master ? line.find('{') : line.find('[');
const auto end = master ? line.rfind('}') : line.rfind(']');
ASSERT(begin != std::string::npos && end != std::string::npos);
const std::string patch_name{line.begin() + begin + 1, line.begin() + end};
CheatList::Block block{};
while (i < lines.size() - 1) {
line = lines[++i];
if (!line.empty() && (line[0] == '[' || line[0] == '{')) {
--i;
break;
}
if (line.size() < 8)
continue;
Cheat out{};
out.raw = ParseSingleLineCheat(line);
block.push_back(out);
}
(master ? master_list : standard_list).emplace_back(patch_name, block);
}
}
return MakeCheatList(system, master_list, standard_list);
}
std::array<u8, 16> TextCheatParser::ParseSingleLineCheat(const std::string& line) const {
std::array<u8, 16> out{};
if (line.size() < 8)
return out;
const auto word1 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data(), 8});
std::memcpy(out.data(), word1.data(), sizeof(u32));
if (line.size() < 17 || line[8] != ' ')
return out;
const auto word2 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 9, 8});
std::memcpy(out.data() + sizeof(u32), word2.data(), sizeof(u32));
if (line.size() < 26 || line[17] != ' ') {
// Perform shifting in case value is truncated early.
const auto type = static_cast<CodeType>((out[0] & 0xF0) >> 4);
if (type == CodeType::Loop || type == CodeType::LoadImmediate ||
type == CodeType::StoreIndexed || type == CodeType::RegisterArithmetic) {
std::memcpy(out.data() + 8, out.data() + 4, sizeof(u32));
std::memset(out.data() + 4, 0, sizeof(u32));
}
return out;
}
const auto word3 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 18, 8});
std::memcpy(out.data() + 2 * sizeof(u32), word3.data(), sizeof(u32));
if (line.size() < 35 || line[26] != ' ') {
// Perform shifting in case value is truncated early.
const auto type = static_cast<CodeType>((out[0] & 0xF0) >> 4);
if (type == CodeType::WriteImmediate || type == CodeType::Conditional) {
std::memcpy(out.data() + 12, out.data() + 8, sizeof(u32));
std::memset(out.data() + 8, 0, sizeof(u32));
}
return out;
}
const auto word4 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 27, 8});
std::memcpy(out.data() + 3 * sizeof(u32), word4.data(), sizeof(u32));
return out;
}
namespace {
u64 MemoryReadImpl(u32 width, VAddr addr) {
switch (width) {
case 1:
return Memory::Read8(addr);
case 2:
return Memory::Read16(addr);
case 4:
return Memory::Read32(addr);
case 8:
return Memory::Read64(addr);
default:
UNREACHABLE();
return 0;
}
}
void MemoryWriteImpl(u32 width, VAddr addr, u64 value) {
switch (width) {
case 1:
Memory::Write8(addr, static_cast<u8>(value));
break;
case 2:
Memory::Write16(addr, static_cast<u16>(value));
break;
case 4:
Memory::Write32(addr, static_cast<u32>(value));
break;
case 8:
Memory::Write64(addr, value);
break;
default:
UNREACHABLE();
}
}
} // Anonymous namespace
CheatEngine::CheatEngine(Core::System& system, std::vector<CheatList> cheats_,
const std::string& build_id, VAddr code_region_start,
VAddr code_region_end)
: cheats{std::move(cheats_)}, core_timing{system.CoreTiming()} {
event = core_timing.RegisterEvent(
"CheatEngine::FrameCallback::" + build_id,
[this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); });
core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event);
const auto& vm_manager = system.CurrentProcess()->VMManager();
for (auto& list : this->cheats) {
list.SetMemoryParameters(code_region_start, vm_manager.GetHeapRegionBaseAddress(),
code_region_end, vm_manager.GetHeapRegionEndAddress(),
&MemoryWriteImpl, &MemoryReadImpl);
}
}
CheatEngine::~CheatEngine() {
core_timing.UnscheduleEvent(event, 0);
}
void CheatEngine::FrameCallback(u64 userdata, s64 cycles_late) {
for (auto& list : cheats) {
list.Execute();
}
core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS - cycles_late, event);
}
} // namespace FileSys
-234
View File
@@ -1,234 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <map>
#include <set>
#include <vector>
#include "common/bit_field.h"
#include "common/common_types.h"
namespace Core {
class System;
}
namespace Core::Timing {
class CoreTiming;
struct EventType;
} // namespace Core::Timing
namespace FileSys {
enum class CodeType : u32 {
// 0TMR00AA AAAAAAAA YYYYYYYY YYYYYYYY
// Writes a T sized value Y to the address A added to the value of register R in memory domain M
WriteImmediate = 0,
// 1TMC00AA AAAAAAAA YYYYYYYY YYYYYYYY
// Compares the T sized value Y to the value at address A in memory domain M using the
// conditional function C. If success, continues execution. If failure, jumps to the matching
// EndConditional statement.
Conditional = 1,
// 20000000
// Terminates a Conditional or ConditionalInput block.
EndConditional = 2,
// 300R0000 VVVVVVVV
// Starts looping V times, storing the current count in register R.
// Loop block is terminated with a matching 310R0000.
Loop = 3,
// 400R0000 VVVVVVVV VVVVVVVV
// Sets the value of register R to the value V.
LoadImmediate = 4,
// 5TMRI0AA AAAAAAAA
// Sets the value of register R to the value of width T at address A in memory domain M, with
// the current value of R added to the address if I == 1.
LoadIndexed = 5,
// 6T0RIFG0 VVVVVVVV VVVVVVVV
// Writes the value V of width T to the memory address stored in register R. Adds the value of
// register G to the final calculation if F is nonzero. Increments the value of register R by T
// after operation if I is nonzero.
StoreIndexed = 6,
// 7T0RA000 VVVVVVVV
// Performs the arithmetic operation A on the value in register R and the value V of width T,
// storing the result in register R.
RegisterArithmetic = 7,
// 8KKKKKKK
// Checks to see if any of the buttons defined by the bitmask K are pressed. If any are,
// execution continues. If none are, execution skips to the next EndConditional command.
ConditionalInput = 8,
};
enum class MemoryType : u32 {
// Addressed relative to start of main NSO
MainNSO = 0,
// Addressed relative to start of heap
Heap = 1,
};
enum class ArithmeticOp : u32 {
Add = 0,
Sub = 1,
Mult = 2,
LShift = 3,
RShift = 4,
};
enum class ComparisonOp : u32 {
GreaterThan = 1,
GreaterThanEqual = 2,
LessThan = 3,
LessThanEqual = 4,
Equal = 5,
Inequal = 6,
};
union Cheat {
std::array<u8, 16> raw;
BitField<4, 4, CodeType> type;
BitField<0, 4, u32> width; // Can be 1, 2, 4, or 8. Measured in bytes.
BitField<0, 4, u32> end_of_loop;
BitField<12, 4, MemoryType> memory_type;
BitField<8, 4, u32> register_3;
BitField<8, 4, ComparisonOp> comparison_op;
BitField<20, 4, u32> load_from_register;
BitField<20, 4, u32> increment_register;
BitField<20, 4, ArithmeticOp> arithmetic_op;
BitField<16, 4, u32> add_additional_register;
BitField<28, 4, u32> register_6;
u64 Address() const;
u64 ValueWidth(u64 offset) const;
u64 Value(u64 offset, u64 width) const;
u32 KeypadValue() const;
};
class CheatParser;
// Represents a full collection of cheats for a game. The Execute function should be called every
// interval that all cheats should be executed. Clients should not directly instantiate this class
// (hence private constructor), they should instead receive an instance from CheatParser, which
// guarantees the list is always in an acceptable state.
class CheatList {
public:
friend class CheatParser;
using Block = std::vector<Cheat>;
using ProgramSegment = std::vector<std::pair<std::string, Block>>;
// (width in bytes, address, value)
using MemoryWriter = void (*)(u32, VAddr, u64);
// (width in bytes, address) -> value
using MemoryReader = u64 (*)(u32, VAddr);
void SetMemoryParameters(VAddr main_begin, VAddr heap_begin, VAddr main_end, VAddr heap_end,
MemoryWriter writer, MemoryReader reader);
void Execute();
private:
CheatList(const Core::System& system_, ProgramSegment master, ProgramSegment standard);
void ProcessBlockPairs(const Block& block);
void ExecuteSingleCheat(const Cheat& cheat);
void ExecuteBlock(const Block& block);
bool EvaluateConditional(const Cheat& cheat) const;
// Individual cheat operations
void WriteImmediate(const Cheat& cheat);
void BeginConditional(const Cheat& cheat);
void EndConditional(const Cheat& cheat);
void Loop(const Cheat& cheat);
void LoadImmediate(const Cheat& cheat);
void LoadIndexed(const Cheat& cheat);
void StoreIndexed(const Cheat& cheat);
void RegisterArithmetic(const Cheat& cheat);
void BeginConditionalInput(const Cheat& cheat);
VAddr SanitizeAddress(VAddr in) const;
// Master Codes are defined as codes that cannot be disabled and are run prior to all
// others.
ProgramSegment master_list;
// All other codes
ProgramSegment standard_list;
bool in_standard = false;
// 16 (0x0-0xF) scratch registers that can be used by cheats
std::array<u64, 16> scratch{};
MemoryWriter writer = nullptr;
MemoryReader reader = nullptr;
u64 main_region_begin{};
u64 heap_region_begin{};
u64 main_region_end{};
u64 heap_region_end{};
u64 current_block{};
// The current index of the cheat within the current Block
u64 current_index{};
// The 'stack' of the program. When a conditional or loop statement is encountered, its index is
// pushed onto this queue. When a end block is encountered, the condition is checked.
std::map<u64, u64> block_pairs;
std::set<u64> encountered_loops;
const Core::System* system;
};
// Intermediary class that parses a text file or other disk format for storing cheats into a
// CheatList object, that can be used for execution.
class CheatParser {
public:
virtual ~CheatParser();
virtual CheatList Parse(const Core::System& system, const std::vector<u8>& data) const = 0;
protected:
CheatList MakeCheatList(const Core::System& system_, CheatList::ProgramSegment master,
CheatList::ProgramSegment standard) const;
};
// CheatParser implementation that parses text files
class TextCheatParser final : public CheatParser {
public:
~TextCheatParser() override;
CheatList Parse(const Core::System& system, const std::vector<u8>& data) const override;
private:
std::array<u8, 16> ParseSingleLineCheat(const std::string& line) const;
};
// Class that encapsulates a CheatList and manages its interaction with memory and CoreTiming
class CheatEngine final {
public:
CheatEngine(Core::System& system_, std::vector<CheatList> cheats_, const std::string& build_id,
VAddr code_region_start, VAddr code_region_end);
~CheatEngine();
private:
void FrameCallback(u64 userdata, s64 cycles_late);
std::vector<CheatList> cheats;
Core::Timing::EventType* event;
Core::Timing::CoreTiming& core_timing;
};
} // namespace FileSys
+8
View File
@@ -528,6 +528,14 @@ u64 NCA::GetTitleId() const {
return header.title_id;
}
std::array<u8, 16> NCA::GetRightsId() const {
return header.rights_id;
}
u32 NCA::GetSDKVersion() const {
return header.sdk_version;
}
bool NCA::IsUpdate() const {
return is_update;
}
+2
View File
@@ -112,6 +112,8 @@ public:
NCAContentType GetType() const;
u64 GetTitleId() const;
std::array<u8, 0x10> GetRightsId() const;
u32 GetSDKVersion() const;
bool IsUpdate() const;
VirtualFile GetRomFS() const;
+49 -20
View File
@@ -22,6 +22,7 @@
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h"
#include "core/loader/nso.h"
#include "core/memory/cheat_engine.h"
#include "core/settings.h"
namespace FileSys {
@@ -63,7 +64,8 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
if (Settings::values.dump_exefs) {
LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id);
const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id);
const auto dump_dir =
Core::System::GetInstance().GetFileSystemController().GetModificationDumpRoot(title_id);
if (dump_dir != nullptr) {
const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs");
VfsRawCopyD(exefs, exefs_dir);
@@ -88,7 +90,8 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
}
// LayeredExeFS
const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
const auto load_dir =
Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
if (load_dir != nullptr && load_dir->GetSize() > 0) {
auto patch_dirs = load_dir->GetSubdirectories();
std::sort(
@@ -174,7 +177,8 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
if (Settings::values.dump_nso) {
LOG_INFO(Loader, "Dumping NSO for name={}, build_id={}, title_id={:016X}", name, build_id,
title_id);
const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id);
const auto dump_dir =
Core::System::GetInstance().GetFileSystemController().GetModificationDumpRoot(title_id);
if (dump_dir != nullptr) {
const auto nso_dir = GetOrCreateDirectoryRelative(dump_dir, "/nso");
const auto file = nso_dir->CreateFile(fmt::format("{}-{}.nso", name, build_id));
@@ -186,7 +190,13 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id);
const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
const auto load_dir =
Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
if (load_dir == nullptr) {
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
return nso;
}
auto patch_dirs = load_dir->GetSubdirectories();
std::sort(patch_dirs.begin(), patch_dirs.end(),
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
@@ -224,7 +234,13 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
LOG_INFO(Loader, "Querying NSO patch existence for build_id={}", build_id);
const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
const auto load_dir =
Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
if (load_dir == nullptr) {
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
return false;
}
auto patch_dirs = load_dir->GetSubdirectories();
std::sort(patch_dirs.begin(), patch_dirs.end(),
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
@@ -232,9 +248,10 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
return !CollectPatches(patch_dirs, build_id).empty();
}
static std::optional<CheatList> ReadCheatFileFromFolder(const Core::System& system, u64 title_id,
const std::array<u8, 0x20>& build_id_,
const VirtualDir& base_path, bool upper) {
namespace {
std::optional<std::vector<Memory::CheatEntry>> ReadCheatFileFromFolder(
const Core::System& system, u64 title_id, const std::array<u8, 0x20>& build_id_,
const VirtualDir& base_path, bool upper) {
const auto build_id_raw = Common::HexToString(build_id_, upper);
const auto build_id = build_id_raw.substr(0, sizeof(u64) * 2);
const auto file = base_path->GetFile(fmt::format("{}.txt", build_id));
@@ -252,31 +269,39 @@ static std::optional<CheatList> ReadCheatFileFromFolder(const Core::System& syst
return std::nullopt;
}
TextCheatParser parser;
return parser.Parse(system, data);
Memory::TextCheatParser parser;
return parser.Parse(
system, std::string_view(reinterpret_cast<const char* const>(data.data()), data.size()));
}
std::vector<CheatList> PatchManager::CreateCheatList(const Core::System& system,
const std::array<u8, 32>& build_id_) const {
const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
} // Anonymous namespace
std::vector<Memory::CheatEntry> PatchManager::CreateCheatList(
const Core::System& system, const std::array<u8, 32>& build_id_) const {
const auto load_dir = system.GetFileSystemController().GetModificationLoadRoot(title_id);
if (load_dir == nullptr) {
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
return {};
}
auto patch_dirs = load_dir->GetSubdirectories();
std::sort(patch_dirs.begin(), patch_dirs.end(),
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
std::vector<CheatList> out;
out.reserve(patch_dirs.size());
std::vector<Memory::CheatEntry> out;
for (const auto& subdir : patch_dirs) {
auto cheats_dir = subdir->GetSubdirectory("cheats");
if (cheats_dir != nullptr) {
auto res = ReadCheatFileFromFolder(system, title_id, build_id_, cheats_dir, true);
if (res.has_value()) {
out.push_back(std::move(*res));
std::copy(res->begin(), res->end(), std::back_inserter(out));
continue;
}
res = ReadCheatFileFromFolder(system, title_id, build_id_, cheats_dir, false);
if (res.has_value())
out.push_back(std::move(*res));
if (res.has_value()) {
std::copy(res->begin(), res->end(), std::back_inserter(out));
}
}
}
@@ -284,7 +309,8 @@ std::vector<CheatList> PatchManager::CreateCheatList(const Core::System& system,
}
static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) {
const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
const auto load_dir =
Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
if ((type != ContentRecordType::Program && type != ContentRecordType::Data) ||
load_dir == nullptr || load_dir->GetSize() <= 0) {
return;
@@ -393,6 +419,8 @@ static bool IsDirValidAndNonEmpty(const VirtualDir& dir) {
std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames(
VirtualFile update_raw) const {
if (title_id == 0)
return {};
std::map<std::string, std::string, std::less<>> out;
const auto& installed = Core::System::GetInstance().GetContentProvider();
const auto& disabled = Settings::values.disabled_addons[title_id];
@@ -423,7 +451,8 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
}
// General Mods (LayeredFS and IPS)
const auto mod_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
const auto mod_dir =
Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
if (mod_dir != nullptr && mod_dir->GetSize() > 0) {
for (const auto& mod : mod_dir->GetSubdirectories()) {
std::string types;
+3 -3
View File
@@ -8,9 +8,9 @@
#include <memory>
#include <string>
#include "common/common_types.h"
#include "core/file_sys/cheat_engine.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/vfs.h"
#include "core/memory/dmnt_cheat_types.h"
namespace Core {
class System;
@@ -51,8 +51,8 @@ public:
bool HasNSOPatch(const std::array<u8, 0x20>& build_id) const;
// Creates a CheatList object with all
std::vector<CheatList> CreateCheatList(const Core::System& system,
const std::array<u8, 0x20>& build_id) const;
std::vector<Memory::CheatEntry> CreateCheatList(const Core::System& system,
const std::array<u8, 0x20>& build_id) const;
// Currently tracked RomFS patches:
// - Game Updates
+173 -15
View File
@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <algorithm>
#include <random>
#include <regex>
#include <mbedtls/sha256.h>
#include "common/assert.h"
@@ -48,18 +49,21 @@ static bool FollowsTwoDigitDirFormat(std::string_view name) {
static bool FollowsNcaIdFormat(std::string_view name) {
static const std::regex nca_id_regex("[0-9A-F]{32}\\.nca", std::regex_constants::ECMAScript |
std::regex_constants::icase);
return name.size() == 36 && std::regex_match(name.begin(), name.end(), nca_id_regex);
static const std::regex nca_id_cnmt_regex(
"[0-9A-F]{32}\\.cnmt.nca", std::regex_constants::ECMAScript | std::regex_constants::icase);
return (name.size() == 36 && std::regex_match(name.begin(), name.end(), nca_id_regex)) ||
(name.size() == 41 && std::regex_match(name.begin(), name.end(), nca_id_cnmt_regex));
}
static std::string GetRelativePathFromNcaID(const std::array<u8, 16>& nca_id, bool second_hex_upper,
bool within_two_digit) {
if (!within_two_digit) {
return fmt::format("/{}.nca", Common::HexToString(nca_id, second_hex_upper));
}
bool within_two_digit, bool cnmt_suffix) {
if (!within_two_digit)
return fmt::format(cnmt_suffix ? "{}.cnmt.nca" : "/{}.nca",
Common::HexToString(nca_id, second_hex_upper));
Core::Crypto::SHA256Hash hash{};
mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0);
return fmt::format("/000000{:02X}/{}.nca", hash[0],
return fmt::format(cnmt_suffix ? "/000000{:02X}/{}.cnmt.nca" : "/000000{:02X}/{}.nca", hash[0],
Common::HexToString(nca_id, second_hex_upper));
}
@@ -127,6 +131,156 @@ std::vector<ContentProviderEntry> ContentProvider::ListEntries() const {
return ListEntriesFilter(std::nullopt, std::nullopt, std::nullopt);
}
PlaceholderCache::PlaceholderCache(VirtualDir dir_) : dir(std::move(dir_)) {}
bool PlaceholderCache::Create(const NcaID& id, u64 size) const {
const auto path = GetRelativePathFromNcaID(id, false, true, false);
if (dir->GetFileRelative(path) != nullptr) {
return false;
}
Core::Crypto::SHA256Hash hash{};
mbedtls_sha256(id.data(), id.size(), hash.data(), 0);
const auto dirname = fmt::format("000000{:02X}", hash[0]);
const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname);
if (dir2 == nullptr)
return false;
const auto file = dir2->CreateFile(fmt::format("{}.nca", Common::HexToString(id, false)));
if (file == nullptr)
return false;
return file->Resize(size);
}
bool PlaceholderCache::Delete(const NcaID& id) const {
const auto path = GetRelativePathFromNcaID(id, false, true, false);
if (dir->GetFileRelative(path) == nullptr) {
return false;
}
Core::Crypto::SHA256Hash hash{};
mbedtls_sha256(id.data(), id.size(), hash.data(), 0);
const auto dirname = fmt::format("000000{:02X}", hash[0]);
const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname);
const auto res = dir2->DeleteFile(fmt::format("{}.nca", Common::HexToString(id, false)));
return res;
}
bool PlaceholderCache::Exists(const NcaID& id) const {
const auto path = GetRelativePathFromNcaID(id, false, true, false);
return dir->GetFileRelative(path) != nullptr;
}
bool PlaceholderCache::Write(const NcaID& id, u64 offset, const std::vector<u8>& data) const {
const auto path = GetRelativePathFromNcaID(id, false, true, false);
const auto file = dir->GetFileRelative(path);
if (file == nullptr)
return false;
return file->WriteBytes(data, offset) == data.size();
}
bool PlaceholderCache::Register(RegisteredCache* cache, const NcaID& placeholder,
const NcaID& install) const {
const auto path = GetRelativePathFromNcaID(placeholder, false, true, false);
const auto file = dir->GetFileRelative(path);
if (file == nullptr)
return false;
const auto res = cache->RawInstallNCA(NCA{file}, &VfsRawCopy, false, install);
if (res != InstallResult::Success)
return false;
return Delete(placeholder);
}
bool PlaceholderCache::CleanAll() const {
return dir->GetParentDirectory()->CleanSubdirectoryRecursive(dir->GetName());
}
std::optional<std::array<u8, 0x10>> PlaceholderCache::GetRightsID(const NcaID& id) const {
const auto path = GetRelativePathFromNcaID(id, false, true, false);
const auto file = dir->GetFileRelative(path);
if (file == nullptr)
return std::nullopt;
NCA nca{file};
if (nca.GetStatus() != Loader::ResultStatus::Success &&
nca.GetStatus() != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) {
return std::nullopt;
}
const auto rights_id = nca.GetRightsId();
if (rights_id == NcaID{})
return std::nullopt;
return rights_id;
}
u64 PlaceholderCache::Size(const NcaID& id) const {
const auto path = GetRelativePathFromNcaID(id, false, true, false);
const auto file = dir->GetFileRelative(path);
if (file == nullptr)
return 0;
return file->GetSize();
}
bool PlaceholderCache::SetSize(const NcaID& id, u64 new_size) const {
const auto path = GetRelativePathFromNcaID(id, false, true, false);
const auto file = dir->GetFileRelative(path);
if (file == nullptr)
return false;
return file->Resize(new_size);
}
std::vector<NcaID> PlaceholderCache::List() const {
std::vector<NcaID> out;
for (const auto& sdir : dir->GetSubdirectories()) {
for (const auto& file : sdir->GetFiles()) {
const auto name = file->GetName();
if (name.length() == 36 && name[32] == '.' && name[33] == 'n' && name[34] == 'c' &&
name[35] == 'a') {
out.push_back(Common::HexStringToArray<0x10>(name.substr(0, 32)));
}
}
}
return out;
}
NcaID PlaceholderCache::Generate() {
std::random_device device;
std::mt19937 gen(device());
std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
NcaID out{};
const auto v1 = distribution(gen);
const auto v2 = distribution(gen);
std::memcpy(out.data(), &v1, sizeof(u64));
std::memcpy(out.data() + sizeof(u64), &v2, sizeof(u64));
return out;
}
VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
std::string_view path) const {
const auto file = dir->GetFileRelative(path);
@@ -169,14 +323,18 @@ VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
VirtualFile RegisteredCache::GetFileAtID(NcaID id) const {
VirtualFile file;
// Try all four modes of file storage:
// (bit 1 = uppercase/lower, bit 0 = within a two-digit dir)
// 00: /000000**/{:032X}.nca
// 01: /{:032X}.nca
// 10: /000000**/{:032x}.nca
// 11: /{:032x}.nca
for (u8 i = 0; i < 4; ++i) {
const auto path = GetRelativePathFromNcaID(id, (i & 0b10) == 0, (i & 0b01) == 0);
// Try all five relevant modes of file storage:
// (bit 2 = uppercase/lower, bit 1 = within a two-digit dir, bit 0 = .cnmt suffix)
// 000: /000000**/{:032X}.nca
// 010: /{:032X}.nca
// 100: /000000**/{:032x}.nca
// 110: /{:032x}.nca
// 111: /{:032x}.cnmt.nca
for (u8 i = 0; i < 8; ++i) {
if ((i % 2) == 1 && i != 7)
continue;
const auto path =
GetRelativePathFromNcaID(id, (i & 0b100) == 0, (i & 0b010) == 0, (i & 0b001) == 0b001);
file = OpenFileOrDirectoryConcat(dir, path);
if (file != nullptr)
return file;
@@ -472,7 +630,7 @@ InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFuncti
memcpy(id.data(), hash.data(), 16);
}
std::string path = GetRelativePathFromNcaID(id, false, true);
std::string path = GetRelativePathFromNcaID(id, false, true, false);
if (GetFileAtID(id) != nullptr && !overwrite_if_exists) {
LOG_WARNING(Loader, "Attempting to overwrite existing NCA. Skipping...");
+25
View File
@@ -25,6 +25,8 @@ enum class NCAContentType : u8;
enum class TitleType : u8;
struct ContentRecord;
struct MetaRecord;
class RegisteredCache;
using NcaID = std::array<u8, 0x10>;
using ContentProviderParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>;
@@ -89,6 +91,27 @@ protected:
Core::Crypto::KeyManager keys;
};
class PlaceholderCache {
public:
explicit PlaceholderCache(VirtualDir dir);
bool Create(const NcaID& id, u64 size) const;
bool Delete(const NcaID& id) const;
bool Exists(const NcaID& id) const;
bool Write(const NcaID& id, u64 offset, const std::vector<u8>& data) const;
bool Register(RegisteredCache* cache, const NcaID& placeholder, const NcaID& install) const;
bool CleanAll() const;
std::optional<std::array<u8, 0x10>> GetRightsID(const NcaID& id) const;
u64 Size(const NcaID& id) const;
bool SetSize(const NcaID& id, u64 new_size) const;
std::vector<NcaID> List() const;
static NcaID Generate();
private:
VirtualDir dir;
};
/*
* A class that catalogues NCAs in the registered directory structure.
* Nintendo's registered format follows this structure:
@@ -103,6 +126,8 @@ protected:
* when 4GB splitting can be ignored.)
*/
class RegisteredCache : public ContentProvider {
friend class PlaceholderCache;
public:
// Parsing function defines the conversion from raw file to NCA. If there are other steps
// besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom
+12 -6
View File
@@ -7,6 +7,7 @@
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/file_sys/card_image.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/patch_manager.h"
@@ -34,16 +35,17 @@ void RomFSFactory::SetPackedUpdate(VirtualFile update_raw) {
this->update_raw = std::move(update_raw);
}
ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() {
ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess(u64 current_process_title_id) const {
if (!updatable)
return MakeResult<VirtualFile>(file);
const PatchManager patch_manager(Core::CurrentProcess()->GetTitleID());
const PatchManager patch_manager(current_process_title_id);
return MakeResult<VirtualFile>(
patch_manager.PatchRomFS(file, ivfc_offset, ContentRecordType::Program, update_raw));
}
ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, ContentRecordType type) {
ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage,
ContentRecordType type) const {
std::shared_ptr<NCA> res;
switch (storage) {
@@ -51,13 +53,17 @@ ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, Conte
res = Core::System::GetInstance().GetContentProvider().GetEntry(title_id, type);
break;
case StorageId::NandSystem:
res = Service::FileSystem::GetSystemNANDContents()->GetEntry(title_id, type);
res =
Core::System::GetInstance().GetFileSystemController().GetSystemNANDContents()->GetEntry(
title_id, type);
break;
case StorageId::NandUser:
res = Service::FileSystem::GetUserNANDContents()->GetEntry(title_id, type);
res = Core::System::GetInstance().GetFileSystemController().GetUserNANDContents()->GetEntry(
title_id, type);
break;
case StorageId::SdCard:
res = Service::FileSystem::GetSDMCContents()->GetEntry(title_id, type);
res = Core::System::GetInstance().GetFileSystemController().GetSDMCContents()->GetEntry(
title_id, type);
break;
default:
UNIMPLEMENTED_MSG("Unimplemented storage_id={:02X}", static_cast<u8>(storage));
+2 -2
View File
@@ -33,8 +33,8 @@ public:
~RomFSFactory();
void SetPackedUpdate(VirtualFile update_raw);
ResultVal<VirtualFile> OpenCurrentProcess();
ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type);
ResultVal<VirtualFile> OpenCurrentProcess(u64 current_process_title_id) const;
ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type) const;
private:
VirtualFile file;
+45 -30
View File
@@ -15,22 +15,8 @@ namespace FileSys {
constexpr char SAVE_DATA_SIZE_FILENAME[] = ".yuzu_save_size";
std::string SaveDataDescriptor::DebugInfo() const {
return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}, "
"rank={}, index={}]",
static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id,
static_cast<u8>(rank), index);
}
SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {
// Delete all temporary storages
// On hardware, it is expected that temporary storage be empty at first use.
dir->DeleteSubdirectoryRecursive("temp");
}
SaveDataFactory::~SaveDataFactory() = default;
ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataDescriptor& meta) {
namespace {
void PrintSaveDataDescriptorWarnings(SaveDataDescriptor meta) {
if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) {
if (meta.zero_1 != 0) {
LOG_WARNING(Service_FS,
@@ -65,23 +51,51 @@ ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, const SaveDat
"non-zero ({:016X}{:016X})",
meta.user_id[1], meta.user_id[0]);
}
}
} // Anonymous namespace
std::string save_directory =
std::string SaveDataDescriptor::DebugInfo() const {
return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, "
"save_id={:016X}, "
"rank={}, index={}]",
static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id,
static_cast<u8>(rank), index);
}
SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {
// Delete all temporary storages
// On hardware, it is expected that temporary storage be empty at first use.
dir->DeleteSubdirectoryRecursive("temp");
}
SaveDataFactory::~SaveDataFactory() = default;
ResultVal<VirtualDir> SaveDataFactory::Create(SaveDataSpaceId space,
const SaveDataDescriptor& meta) const {
PrintSaveDataDescriptorWarnings(meta);
const auto save_directory =
GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id);
// TODO(DarkLordZach): Try to not create when opening, there are dedicated create save methods.
// But, user_ids don't match so this works for now.
auto out = dir->CreateDirectoryRelative(save_directory);
// Return an error if the save data doesn't actually exist.
if (out == nullptr) {
// TODO(DarkLordZach): Find out correct error code.
return ResultCode(-1);
}
return MakeResult<VirtualDir>(std::move(out));
}
ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space,
const SaveDataDescriptor& meta) const {
const auto save_directory =
GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id);
auto out = dir->GetDirectoryRelative(save_directory);
if (out == nullptr) {
// TODO(bunnei): This is a work-around to always create a save data directory if it does not
// already exist. This is a hack, as we do not understand yet how this works on hardware.
// Without a save data directory, many games will assert on boot. This should not have any
// bad side-effects.
out = dir->CreateDirectoryRelative(save_directory);
}
// Return an error if the save data doesn't actually exist.
if (out == nullptr) {
// TODO(Subv): Find out correct error code.
@@ -113,8 +127,9 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
u128 user_id, u64 save_id) {
// According to switchbrew, if a save is of type SaveData and the title id field is 0, it should
// be interpreted as the title id of the current process.
if (type == SaveDataType::SaveData && title_id == 0)
title_id = Core::CurrentProcess()->GetTitleID();
if (type == SaveDataType::SaveData && title_id == 0) {
title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
}
std::string out = GetSaveDataSpaceIdPath(space);
@@ -152,7 +167,7 @@ SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
}
void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
SaveDataSize new_value) {
SaveDataSize new_value) const {
const auto path = GetFullPath(SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
const auto dir = GetOrCreateDirectoryRelative(this->dir, path);
+4 -2
View File
@@ -64,7 +64,8 @@ public:
explicit SaveDataFactory(VirtualDir dir);
~SaveDataFactory();
ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataDescriptor& meta);
ResultVal<VirtualDir> Create(SaveDataSpaceId space, const SaveDataDescriptor& meta) const;
ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataDescriptor& meta) const;
VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const;
@@ -73,7 +74,8 @@ public:
u128 user_id, u64 save_id);
SaveDataSize ReadSaveDataSize(SaveDataType type, u64 title_id, u128 user_id) const;
void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, SaveDataSize new_value);
void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
SaveDataSize new_value) const;
private:
VirtualDir dir;
+25 -2
View File
@@ -6,6 +6,7 @@
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/sdmc_factory.h"
#include "core/file_sys/xts_archive.h"
#include "core/settings.h"
namespace FileSys {
@@ -14,16 +15,38 @@ SDMCFactory::SDMCFactory(VirtualDir dir_)
GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/registered"),
[](const VirtualFile& file, const NcaID& id) {
return NAX{file, id}.GetDecrypted();
})) {}
})),
placeholder(std::make_unique<PlaceholderCache>(
GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/placehld"))) {}
SDMCFactory::~SDMCFactory() = default;
ResultVal<VirtualDir> SDMCFactory::Open() {
ResultVal<VirtualDir> SDMCFactory::Open() const {
return MakeResult<VirtualDir>(dir);
}
VirtualDir SDMCFactory::GetSDMCContentDirectory() const {
return GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents");
}
RegisteredCache* SDMCFactory::GetSDMCContents() const {
return contents.get();
}
PlaceholderCache* SDMCFactory::GetSDMCPlaceholder() const {
return placeholder.get();
}
VirtualDir SDMCFactory::GetImageDirectory() const {
return GetOrCreateDirectoryRelative(dir, "/Nintendo/Album");
}
u64 SDMCFactory::GetSDMCFreeSpace() const {
return GetSDMCTotalSpace() - dir->GetSize();
}
u64 SDMCFactory::GetSDMCTotalSpace() const {
return static_cast<u64>(Settings::values.sdmc_size);
}
} // namespace FileSys
+12 -1
View File
@@ -11,6 +11,7 @@
namespace FileSys {
class RegisteredCache;
class PlaceholderCache;
/// File system interface to the SDCard archive
class SDMCFactory {
@@ -18,13 +19,23 @@ public:
explicit SDMCFactory(VirtualDir dir);
~SDMCFactory();
ResultVal<VirtualDir> Open();
ResultVal<VirtualDir> Open() const;
VirtualDir GetSDMCContentDirectory() const;
RegisteredCache* GetSDMCContents() const;
PlaceholderCache* GetSDMCPlaceholder() const;
VirtualDir GetImageDirectory() const;
u64 GetSDMCFreeSpace() const;
u64 GetSDMCTotalSpace() const;
private:
VirtualDir dir;
std::unique_ptr<RegisteredCache> contents;
std::unique_ptr<PlaceholderCache> placeholder;
};
} // namespace FileSys
+28 -1
View File
@@ -14,6 +14,7 @@
#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/program_metadata.h"
#include "core/file_sys/submission_package.h"
#include "core/loader/loader.h"
@@ -78,6 +79,10 @@ Loader::ResultStatus NSP::GetStatus() const {
}
Loader::ResultStatus NSP::GetProgramStatus(u64 title_id) const {
if (IsExtractedType() && GetExeFS() != nullptr && FileSys::IsDirectoryExeFS(GetExeFS())) {
return Loader::ResultStatus::Success;
}
const auto iter = program_status.find(title_id);
if (iter == program_status.end())
return Loader::ResultStatus::ErrorNSPMissingProgramNCA;
@@ -85,12 +90,29 @@ Loader::ResultStatus NSP::GetProgramStatus(u64 title_id) const {
}
u64 NSP::GetFirstTitleID() const {
if (IsExtractedType()) {
return GetProgramTitleID();
}
if (program_status.empty())
return 0;
return program_status.begin()->first;
}
u64 NSP::GetProgramTitleID() const {
if (IsExtractedType()) {
if (GetExeFS() == nullptr || !IsDirectoryExeFS(GetExeFS())) {
return 0;
}
ProgramMetadata meta;
if (meta.Load(GetExeFS()->GetFile("main.npdm")) == Loader::ResultStatus::Success) {
return meta.GetTitleID();
} else {
return 0;
}
}
const auto out = GetFirstTitleID();
if ((out & 0x800) == 0)
return out;
@@ -102,6 +124,10 @@ u64 NSP::GetProgramTitleID() const {
}
std::vector<u64> NSP::GetTitleIDs() const {
if (IsExtractedType()) {
return {GetProgramTitleID()};
}
std::vector<u64> out;
out.reserve(ncas.size());
for (const auto& kv : ncas)
@@ -222,7 +248,8 @@ void NSP::InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files) {
void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
for (const auto& outer_file : files) {
if (outer_file->GetName().substr(outer_file->GetName().size() - 9) != ".cnmt.nca") {
if (outer_file->GetName().size() < 9 ||
outer_file->GetName().substr(outer_file->GetName().size() - 9) != ".cnmt.nca") {
continue;
}
+79
View File
@@ -0,0 +1,79 @@
// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <string>
#include <zip.h>
#include "common/logging/backend.h"
#include "core/file_sys/vfs.h"
#include "core/file_sys/vfs_libzip.h"
#include "core/file_sys/vfs_vector.h"
namespace FileSys {
VirtualDir ExtractZIP(VirtualFile file) {
zip_error_t error{};
const auto data = file->ReadAllBytes();
std::unique_ptr<zip_source_t, decltype(&zip_source_close)> src{
zip_source_buffer_create(data.data(), data.size(), 0, &error), zip_source_close};
if (src == nullptr)
return nullptr;
std::unique_ptr<zip_t, decltype(&zip_close)> zip{zip_open_from_source(src.get(), 0, &error),
zip_close};
if (zip == nullptr)
return nullptr;
std::shared_ptr<VectorVfsDirectory> out = std::make_shared<VectorVfsDirectory>();
const auto num_entries = zip_get_num_entries(zip.get(), 0);
zip_stat_t stat{};
zip_stat_init(&stat);
for (std::size_t i = 0; i < num_entries; ++i) {
const auto stat_res = zip_stat_index(zip.get(), i, 0, &stat);
if (stat_res == -1)
return nullptr;
const std::string name(stat.name);
if (name.empty())
continue;
if (name.back() != '/') {
std::unique_ptr<zip_file_t, decltype(&zip_fclose)> file{
zip_fopen_index(zip.get(), i, 0), zip_fclose};
std::vector<u8> buf(stat.size);
if (zip_fread(file.get(), buf.data(), buf.size()) != buf.size())
return nullptr;
const auto parts = FileUtil::SplitPathComponents(stat.name);
const auto new_file = std::make_shared<VectorVfsFile>(buf, parts.back());
std::shared_ptr<VectorVfsDirectory> dtrv = out;
for (std::size_t j = 0; j < parts.size() - 1; ++j) {
if (dtrv == nullptr)
return nullptr;
const auto subdir = dtrv->GetSubdirectory(parts[j]);
if (subdir == nullptr) {
const auto temp = std::make_shared<VectorVfsDirectory>(
std::vector<VirtualFile>{}, std::vector<VirtualDir>{}, parts[j]);
dtrv->AddDirectory(temp);
dtrv = temp;
} else {
dtrv = std::dynamic_pointer_cast<VectorVfsDirectory>(subdir);
}
}
if (dtrv == nullptr)
return nullptr;
dtrv->AddFile(new_file);
}
}
return out;
}
} // namespace FileSys
+13
View File
@@ -0,0 +1,13 @@
// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/file_sys/vfs_types.h"
namespace FileSys {
VirtualDir ExtractZIP(VirtualFile zip);
} // namespace FileSys
+15 -20
View File
@@ -202,13 +202,11 @@ void RegisterModule(std::string name, VAddr beg, VAddr end, bool add_elf_ext) {
}
static Kernel::Thread* FindThreadById(s64 id) {
for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
const auto& threads = Core::System::GetInstance().Scheduler(core).GetThreadList();
for (auto& thread : threads) {
if (thread->GetThreadID() == static_cast<u64>(id)) {
current_core = core;
return thread.get();
}
const auto& threads = Core::System::GetInstance().GlobalScheduler().GetThreadList();
for (auto& thread : threads) {
if (thread->GetThreadID() == static_cast<u64>(id)) {
current_core = thread->GetProcessorID();
return thread.get();
}
}
return nullptr;
@@ -641,16 +639,15 @@ static void HandleQuery() {
strlen("Xfer:features:read:target.xml:")) == 0) {
SendReply(target_xml);
} else if (strncmp(query, "Offsets", strlen("Offsets")) == 0) {
const VAddr base_address = Core::CurrentProcess()->VMManager().GetCodeRegionBaseAddress();
const VAddr base_address =
Core::System::GetInstance().CurrentProcess()->VMManager().GetCodeRegionBaseAddress();
std::string buffer = fmt::format("TextSeg={:0x}", base_address);
SendReply(buffer.c_str());
} else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) {
std::string val = "m";
for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
const auto& threads = Core::System::GetInstance().Scheduler(core).GetThreadList();
for (const auto& thread : threads) {
val += fmt::format("{:x},", thread->GetThreadID());
}
const auto& threads = Core::System::GetInstance().GlobalScheduler().GetThreadList();
for (const auto& thread : threads) {
val += fmt::format("{:x},", thread->GetThreadID());
}
val.pop_back();
SendReply(val.c_str());
@@ -660,13 +657,11 @@ static void HandleQuery() {
std::string buffer;
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();
for (const auto& thread : threads) {
buffer +=
fmt::format(R"*(<thread id="{:x}" core="{:d}" name="Thread {:x}"></thread>)*",
thread->GetThreadID(), core, thread->GetThreadID());
}
const auto& threads = Core::System::GetInstance().GlobalScheduler().GetThreadList();
for (const auto& thread : threads) {
buffer +=
fmt::format(R"*(<thread id="{:x}" core="{:d}" name="Thread {:x}"></thread>)*",
thread->GetThreadID(), thread->GetProcessorID(), thread->GetThreadID());
}
buffer += "</threads>";
SendReply(buffer.c_str());
+11 -17
View File
@@ -22,6 +22,7 @@ namespace Kernel {
namespace {
// Wake up num_to_wake (or all) threads in a vector.
void WakeThreads(const std::vector<SharedPtr<Thread>>& waiting_threads, s32 num_to_wake) {
auto& system = Core::System::GetInstance();
// Only process up to 'target' threads, unless 'target' is <= 0, in which case process
// them all.
std::size_t last = waiting_threads.size();
@@ -35,6 +36,7 @@ void WakeThreads(const std::vector<SharedPtr<Thread>>& waiting_threads, s32 num_
waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS);
waiting_threads[i]->SetArbiterWaitAddress(0);
waiting_threads[i]->ResumeFromWait();
system.PrepareReschedule(waiting_threads[i]->GetProcessorID());
}
}
} // Anonymous namespace
@@ -169,30 +171,22 @@ ResultCode AddressArbiter::WaitForAddressImpl(VAddr address, s64 timeout) {
current_thread->WakeAfterDelay(timeout);
system.CpuCore(current_thread->GetProcessorID()).PrepareReschedule();
system.PrepareReschedule(current_thread->GetProcessorID());
return RESULT_TIMEOUT;
}
std::vector<SharedPtr<Thread>> AddressArbiter::GetThreadsWaitingOnAddress(VAddr address) const {
const auto RetrieveWaitingThreads = [this](std::size_t core_index,
std::vector<SharedPtr<Thread>>& waiting_threads,
VAddr arb_addr) {
const auto& scheduler = system.Scheduler(core_index);
const auto& thread_list = scheduler.GetThreadList();
for (const auto& thread : thread_list) {
if (thread->GetArbiterWaitAddress() == arb_addr) {
waiting_threads.push_back(thread);
}
}
};
// Retrieve all threads that are waiting for this address.
std::vector<SharedPtr<Thread>> threads;
RetrieveWaitingThreads(0, threads, address);
RetrieveWaitingThreads(1, threads, address);
RetrieveWaitingThreads(2, threads, address);
RetrieveWaitingThreads(3, threads, address);
const auto& scheduler = system.GlobalScheduler();
const auto& thread_list = scheduler.GetThreadList();
for (const auto& thread : thread_list) {
if (thread->GetArbiterWaitAddress() == address) {
threads.push_back(thread);
}
}
// Sort them by priority, such that the highest priority ones come first.
std::sort(threads.begin(), threads.end(),
+1 -1
View File
@@ -103,7 +103,7 @@ SharedPtr<Object> HandleTable::GetGeneric(Handle handle) const {
if (handle == CurrentThread) {
return GetCurrentThread();
} else if (handle == CurrentProcess) {
return Core::CurrentProcess();
return Core::System::GetInstance().CurrentProcess();
}
if (!IsValid(handle)) {
+11 -1
View File
@@ -18,6 +18,7 @@
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/lock.h"
#include "core/hle/result.h"
@@ -88,7 +89,7 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] s64 cycles_
}
struct KernelCore::Impl {
explicit Impl(Core::System& system) : system{system} {}
explicit Impl(Core::System& system) : system{system}, global_scheduler{system} {}
void Initialize(KernelCore& kernel) {
Shutdown();
@@ -140,6 +141,7 @@ struct KernelCore::Impl {
// Lists all processes that exist in the current session.
std::vector<SharedPtr<Process>> process_list;
Process* current_process = nullptr;
Kernel::GlobalScheduler global_scheduler;
SharedPtr<ResourceLimit> system_resource_limit;
@@ -203,6 +205,14 @@ const std::vector<SharedPtr<Process>>& KernelCore::GetProcessList() const {
return impl->process_list;
}
Kernel::GlobalScheduler& KernelCore::GlobalScheduler() {
return impl->global_scheduler;
}
const Kernel::GlobalScheduler& KernelCore::GlobalScheduler() const {
return impl->global_scheduler;
}
void KernelCore::AddNamedPort(std::string name, SharedPtr<ClientPort> port) {
impl->named_ports.emplace(std::move(name), std::move(port));
}
+7
View File
@@ -25,6 +25,7 @@ class HandleTable;
class Process;
class ResourceLimit;
class Thread;
class GlobalScheduler;
/// Represents a single instance of the kernel.
class KernelCore {
@@ -75,6 +76,12 @@ public:
/// Retrieves the list of processes.
const std::vector<SharedPtr<Process>>& GetProcessList() const;
/// Gets the sole instance of the global scheduler
Kernel::GlobalScheduler& GlobalScheduler();
/// Gets the sole instance of the global scheduler
const Kernel::GlobalScheduler& GlobalScheduler() const;
/// Adds a port to the named port table
void AddNamedPort(std::string name, SharedPtr<ClientPort> port);
+24 -5
View File
@@ -7,6 +7,7 @@
#include "common/assert.h"
#include "core/core.h"
#include "core/core_cpu.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
@@ -78,7 +79,7 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
// thread.
ASSERT(requesting_thread == current_thread);
const u32 addr_value = Memory::Read32(address);
u32 addr_value = Memory::Read32(address);
// If the mutex isn't being held, just return success.
if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) {
@@ -89,6 +90,20 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
return ERR_INVALID_HANDLE;
}
// This a workaround where an unknown bug writes the mutex value to give ownership to a cond var
// waiting thread.
if (holding_thread->GetStatus() == ThreadStatus::WaitCondVar) {
if (holding_thread->GetMutexWaitAddress() == address) {
Release(address, holding_thread.get());
addr_value = Memory::Read32(address);
if (addr_value == 0)
return RESULT_SUCCESS;
else {
holding_thread = handle_table.Get<Thread>(addr_value & Mutex::MutexOwnerMask);
}
}
}
// Wait until the mutex is released
current_thread->SetMutexWaitAddress(address);
current_thread->SetWaitHandle(requesting_thread_handle);
@@ -104,14 +119,13 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
return RESULT_SUCCESS;
}
ResultCode Mutex::Release(VAddr address) {
ResultCode Mutex::Release(VAddr address, Thread* holding_thread) {
// The mutex address must be 4-byte aligned
if ((address % sizeof(u32)) != 0) {
return ERR_INVALID_ADDRESS;
}
auto* const current_thread = system.CurrentScheduler().GetCurrentThread();
auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(current_thread, address);
auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(holding_thread, address);
// There are no more threads waiting for the mutex, release it completely.
if (thread == nullptr) {
@@ -120,7 +134,7 @@ ResultCode Mutex::Release(VAddr address) {
}
// Transfer the ownership of the mutex from the previous owner to the new one.
TransferMutexOwnership(address, current_thread, thread);
TransferMutexOwnership(address, holding_thread, thread);
u32 mutex_value = thread->GetWaitHandle();
@@ -140,6 +154,11 @@ ResultCode Mutex::Release(VAddr address) {
thread->SetMutexWaitAddress(0);
thread->SetWaitHandle(0);
if (thread->GetProcessorID() >= 0)
system.CpuCore(thread->GetProcessorID()).PrepareReschedule();
if (holding_thread->GetProcessorID() >= 0)
system.CpuCore(holding_thread->GetProcessorID()).PrepareReschedule();
return RESULT_SUCCESS;
}
} // namespace Kernel
+1 -1
View File
@@ -29,7 +29,7 @@ public:
Handle requesting_thread_handle);
/// Releases the mutex at the specified address.
ResultCode Release(VAddr address);
ResultCode Release(VAddr address, Thread* holding_thread);
private:
Core::System& system;
+1 -4
View File
@@ -213,10 +213,7 @@ void Process::PrepareForTermination() {
}
};
stop_threads(system.Scheduler(0).GetThreadList());
stop_threads(system.Scheduler(1).GetThreadList());
stop_threads(system.Scheduler(2).GetThreadList());
stop_threads(system.Scheduler(3).GetThreadList());
stop_threads(system.GlobalScheduler().GetThreadList());
FreeTLSRegion(tls_region_address);
tls_region_address = 0;
+290 -155
View File
@@ -1,8 +1,13 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
//
// SelectThreads, Yield functions originally by TuxSH.
// licensed under GPLv2 or later under exception provided by the author.
#include <algorithm>
#include <set>
#include <unordered_set>
#include <utility>
#include "common/assert.h"
@@ -17,56 +22,277 @@
namespace Kernel {
std::mutex Scheduler::scheduler_mutex;
GlobalScheduler::GlobalScheduler(Core::System& system) : system{system} {
reselection_pending = false;
}
Scheduler::Scheduler(Core::System& system, Core::ARM_Interface& cpu_core)
: cpu_core{cpu_core}, system{system} {}
void GlobalScheduler::AddThread(SharedPtr<Thread> thread) {
thread_list.push_back(std::move(thread));
}
Scheduler::~Scheduler() {
for (auto& thread : thread_list) {
thread->Stop();
void GlobalScheduler::RemoveThread(const Thread* thread) {
thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread),
thread_list.end());
}
/*
* UnloadThread selects a core and forces it to unload its current thread's context
*/
void GlobalScheduler::UnloadThread(s32 core) {
Scheduler& sched = system.Scheduler(core);
sched.UnloadThread();
}
/*
* SelectThread takes care of selecting the new scheduled thread.
* It does it in 3 steps:
* - First a thread is selected from the top of the priority queue. If no thread
* is obtained then we move to step two, else we are done.
* - Second we try to get a suggested thread that's not assigned to any core or
* that is not the top thread in that core.
* - Third is no suggested thread is found, we do a second pass and pick a running
* thread in another core and swap it with its current thread.
*/
void GlobalScheduler::SelectThread(u32 core) {
const auto update_thread = [](Thread* thread, Scheduler& sched) {
if (thread != sched.selected_thread) {
if (thread == nullptr) {
++sched.idle_selection_count;
}
sched.selected_thread = thread;
}
sched.context_switch_pending = sched.selected_thread != sched.current_thread;
std::atomic_thread_fence(std::memory_order_seq_cst);
};
Scheduler& sched = system.Scheduler(core);
Thread* current_thread = nullptr;
// Step 1: Get top thread in schedule queue.
current_thread = scheduled_queue[core].empty() ? nullptr : scheduled_queue[core].front();
if (current_thread) {
update_thread(current_thread, sched);
return;
}
// Step 2: Try selecting a suggested thread.
Thread* winner = nullptr;
std::set<s32> sug_cores;
for (auto thread : suggested_queue[core]) {
s32 this_core = thread->GetProcessorID();
Thread* thread_on_core = nullptr;
if (this_core >= 0) {
thread_on_core = scheduled_queue[this_core].front();
}
if (this_core < 0 || thread != thread_on_core) {
winner = thread;
break;
}
sug_cores.insert(this_core);
}
// if we got a suggested thread, select it, else do a second pass.
if (winner && winner->GetPriority() > 2) {
if (winner->IsRunning()) {
UnloadThread(winner->GetProcessorID());
}
TransferToCore(winner->GetPriority(), core, winner);
update_thread(winner, sched);
return;
}
// Step 3: Select a suggested thread from another core
for (auto& src_core : sug_cores) {
auto it = scheduled_queue[src_core].begin();
it++;
if (it != scheduled_queue[src_core].end()) {
Thread* thread_on_core = scheduled_queue[src_core].front();
Thread* to_change = *it;
if (thread_on_core->IsRunning() || to_change->IsRunning()) {
UnloadThread(src_core);
}
TransferToCore(thread_on_core->GetPriority(), core, thread_on_core);
current_thread = thread_on_core;
break;
}
}
update_thread(current_thread, sched);
}
/*
* YieldThread takes a thread and moves it to the back of the it's priority list
* This operation can be redundant and no scheduling is changed if marked as so.
*/
void GlobalScheduler::YieldThread(Thread* yielding_thread) {
// Note: caller should use critical section, etc.
const u32 core_id = static_cast<u32>(yielding_thread->GetProcessorID());
const u32 priority = yielding_thread->GetPriority();
// Yield the thread
ASSERT_MSG(yielding_thread == scheduled_queue[core_id].front(priority),
"Thread yielding without being in front");
scheduled_queue[core_id].yield(priority);
Thread* winner = scheduled_queue[core_id].front(priority);
AskForReselectionOrMarkRedundant(yielding_thread, winner);
}
/*
* YieldThreadAndBalanceLoad takes a thread and moves it to the back of the it's priority list.
* Afterwards, tries to pick a suggested thread from the suggested queue that has worse time or
* a better priority than the next thread in the core.
* This operation can be redundant and no scheduling is changed if marked as so.
*/
void GlobalScheduler::YieldThreadAndBalanceLoad(Thread* yielding_thread) {
// Note: caller should check if !thread.IsSchedulerOperationRedundant and use critical section,
// etc.
const u32 core_id = static_cast<u32>(yielding_thread->GetProcessorID());
const u32 priority = yielding_thread->GetPriority();
// Yield the thread
ASSERT_MSG(yielding_thread == scheduled_queue[core_id].front(priority),
"Thread yielding without being in front");
scheduled_queue[core_id].yield(priority);
std::array<Thread*, NUM_CPU_CORES> current_threads;
for (u32 i = 0; i < NUM_CPU_CORES; i++) {
current_threads[i] = scheduled_queue[i].empty() ? nullptr : scheduled_queue[i].front();
}
Thread* next_thread = scheduled_queue[core_id].front(priority);
Thread* winner = nullptr;
for (auto& thread : suggested_queue[core_id]) {
const s32 source_core = thread->GetProcessorID();
if (source_core >= 0) {
if (current_threads[source_core] != nullptr) {
if (thread == current_threads[source_core] ||
current_threads[source_core]->GetPriority() < min_regular_priority) {
continue;
}
}
if (next_thread->GetLastRunningTicks() >= thread->GetLastRunningTicks() ||
next_thread->GetPriority() < thread->GetPriority()) {
if (thread->GetPriority() <= priority) {
winner = thread;
break;
}
}
}
}
if (winner != nullptr) {
if (winner != yielding_thread) {
if (winner->IsRunning()) {
UnloadThread(winner->GetProcessorID());
}
TransferToCore(winner->GetPriority(), core_id, winner);
}
} else {
winner = next_thread;
}
AskForReselectionOrMarkRedundant(yielding_thread, winner);
}
/*
* YieldThreadAndWaitForLoadBalancing takes a thread and moves it out of the scheduling queue
* and into the suggested queue. If no thread can be squeduled afterwards in that core,
* a suggested thread is obtained instead.
* This operation can be redundant and no scheduling is changed if marked as so.
*/
void GlobalScheduler::YieldThreadAndWaitForLoadBalancing(Thread* yielding_thread) {
// Note: caller should check if !thread.IsSchedulerOperationRedundant and use critical section,
// etc.
Thread* winner = nullptr;
const u32 core_id = static_cast<u32>(yielding_thread->GetProcessorID());
// Remove the thread from its scheduled mlq, put it on the corresponding "suggested" one instead
TransferToCore(yielding_thread->GetPriority(), -1, yielding_thread);
// If the core is idle, perform load balancing, excluding the threads that have just used this
// function...
if (scheduled_queue[core_id].empty()) {
// Here, "current_threads" is calculated after the ""yield"", unlike yield -1
std::array<Thread*, NUM_CPU_CORES> current_threads;
for (u32 i = 0; i < NUM_CPU_CORES; i++) {
current_threads[i] = scheduled_queue[i].empty() ? nullptr : scheduled_queue[i].front();
}
for (auto& thread : suggested_queue[core_id]) {
const s32 source_core = thread->GetProcessorID();
if (source_core < 0 || thread == current_threads[source_core]) {
continue;
}
if (current_threads[source_core] == nullptr ||
current_threads[source_core]->GetPriority() >= min_regular_priority) {
winner = thread;
}
break;
}
if (winner != nullptr) {
if (winner != yielding_thread) {
if (winner->IsRunning()) {
UnloadThread(winner->GetProcessorID());
}
TransferToCore(winner->GetPriority(), core_id, winner);
}
} else {
winner = yielding_thread;
}
}
AskForReselectionOrMarkRedundant(yielding_thread, winner);
}
void GlobalScheduler::Schedule(u32 priority, u32 core, Thread* thread) {
ASSERT_MSG(thread->GetProcessorID() == core, "Thread must be assigned to this core.");
scheduled_queue[core].add(thread, priority);
}
void GlobalScheduler::SchedulePrepend(u32 priority, u32 core, Thread* thread) {
ASSERT_MSG(thread->GetProcessorID() == core, "Thread must be assigned to this core.");
scheduled_queue[core].add(thread, priority, false);
}
void GlobalScheduler::AskForReselectionOrMarkRedundant(Thread* current_thread, Thread* winner) {
if (current_thread == winner) {
// TODO(blinkhawk): manage redundant operations, this is not implemented.
// as its mostly an optimization.
// current_thread->SetRedundantSchedulerOperation();
} else {
reselection_pending.store(true, std::memory_order_release);
}
}
GlobalScheduler::~GlobalScheduler() = default;
Scheduler::Scheduler(Core::System& system, Core::ARM_Interface& cpu_core, u32 core_id)
: system(system), cpu_core(cpu_core), core_id(core_id) {}
Scheduler::~Scheduler() = default;
bool Scheduler::HaveReadyThreads() const {
std::lock_guard lock{scheduler_mutex};
return !ready_queue.empty();
return system.GlobalScheduler().HaveReadyThreads(core_id);
}
Thread* Scheduler::GetCurrentThread() const {
return current_thread.get();
}
Thread* Scheduler::GetSelectedThread() const {
return selected_thread.get();
}
void Scheduler::SelectThreads() {
system.GlobalScheduler().SelectThread(core_id);
}
u64 Scheduler::GetLastContextSwitchTicks() const {
return last_context_switch_time;
}
Thread* Scheduler::PopNextReadyThread() {
Thread* next = nullptr;
Thread* thread = GetCurrentThread();
if (thread && thread->GetStatus() == ThreadStatus::Running) {
if (ready_queue.empty()) {
return thread;
}
// We have to do better than the current thread.
// This call returns null when that's not possible.
next = ready_queue.front();
if (next == nullptr || next->GetPriority() >= thread->GetPriority()) {
next = thread;
}
} else {
if (ready_queue.empty()) {
return nullptr;
}
next = ready_queue.front();
void Scheduler::TryDoContextSwitch() {
if (context_switch_pending) {
SwitchContext();
}
return next;
}
void Scheduler::SwitchContext(Thread* new_thread) {
Thread* previous_thread = GetCurrentThread();
void Scheduler::UnloadThread() {
Thread* const previous_thread = GetCurrentThread();
Process* const previous_process = system.Kernel().CurrentProcess();
UpdateLastContextSwitchTime(previous_thread, previous_process);
@@ -80,23 +306,52 @@ void Scheduler::SwitchContext(Thread* new_thread) {
if (previous_thread->GetStatus() == ThreadStatus::Running) {
// This is only the case when a reschedule is triggered without the current thread
// yielding execution (i.e. an event triggered, system core time-sliced, etc)
ready_queue.add(previous_thread, previous_thread->GetPriority(), false);
previous_thread->SetStatus(ThreadStatus::Ready);
}
previous_thread->SetIsRunning(false);
}
current_thread = nullptr;
}
void Scheduler::SwitchContext() {
Thread* const previous_thread = GetCurrentThread();
Thread* const new_thread = GetSelectedThread();
context_switch_pending = false;
if (new_thread == previous_thread) {
return;
}
Process* const previous_process = system.Kernel().CurrentProcess();
UpdateLastContextSwitchTime(previous_thread, previous_process);
// Save context for previous thread
if (previous_thread) {
cpu_core.SaveContext(previous_thread->GetContext());
// Save the TPIDR_EL0 system register in case it was modified.
previous_thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
if (previous_thread->GetStatus() == ThreadStatus::Running) {
// This is only the case when a reschedule is triggered without the current thread
// yielding execution (i.e. an event triggered, system core time-sliced, etc)
previous_thread->SetStatus(ThreadStatus::Ready);
}
previous_thread->SetIsRunning(false);
}
// Load context of new thread
if (new_thread) {
ASSERT_MSG(new_thread->GetProcessorID() == this->core_id,
"Thread must be assigned to this core.");
ASSERT_MSG(new_thread->GetStatus() == ThreadStatus::Ready,
"Thread must be ready to become running.");
// Cancel any outstanding wakeup events for this thread
new_thread->CancelWakeupTimer();
current_thread = new_thread;
ready_queue.remove(new_thread, new_thread->GetPriority());
new_thread->SetStatus(ThreadStatus::Running);
new_thread->SetIsRunning(true);
auto* const thread_owner_process = current_thread->GetOwnerProcess();
if (previous_process != thread_owner_process) {
@@ -130,124 +385,4 @@ void Scheduler::UpdateLastContextSwitchTime(Thread* thread, Process* process) {
last_context_switch_time = most_recent_switch_ticks;
}
void Scheduler::Reschedule() {
std::lock_guard lock{scheduler_mutex};
Thread* cur = GetCurrentThread();
Thread* next = PopNextReadyThread();
if (cur && next) {
LOG_TRACE(Kernel, "context switch {} -> {}", cur->GetObjectId(), next->GetObjectId());
} else if (cur) {
LOG_TRACE(Kernel, "context switch {} -> idle", cur->GetObjectId());
} else if (next) {
LOG_TRACE(Kernel, "context switch idle -> {}", next->GetObjectId());
}
SwitchContext(next);
}
void Scheduler::AddThread(SharedPtr<Thread> thread) {
std::lock_guard lock{scheduler_mutex};
thread_list.push_back(std::move(thread));
}
void Scheduler::RemoveThread(Thread* thread) {
std::lock_guard lock{scheduler_mutex};
thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread),
thread_list.end());
}
void Scheduler::ScheduleThread(Thread* thread, u32 priority) {
std::lock_guard lock{scheduler_mutex};
ASSERT(thread->GetStatus() == ThreadStatus::Ready);
ready_queue.add(thread, priority);
}
void Scheduler::UnscheduleThread(Thread* thread, u32 priority) {
std::lock_guard lock{scheduler_mutex};
ASSERT(thread->GetStatus() == ThreadStatus::Ready);
ready_queue.remove(thread, priority);
}
void Scheduler::SetThreadPriority(Thread* thread, u32 priority) {
std::lock_guard lock{scheduler_mutex};
if (thread->GetPriority() == priority) {
return;
}
// If thread was ready, adjust queues
if (thread->GetStatus() == ThreadStatus::Ready)
ready_queue.adjust(thread, thread->GetPriority(), priority);
}
Thread* Scheduler::GetNextSuggestedThread(u32 core, u32 maximum_priority) const {
std::lock_guard lock{scheduler_mutex};
const u32 mask = 1U << core;
for (auto* thread : ready_queue) {
if ((thread->GetAffinityMask() & mask) != 0 && thread->GetPriority() < maximum_priority) {
return thread;
}
}
return nullptr;
}
void Scheduler::YieldWithoutLoadBalancing(Thread* thread) {
ASSERT(thread != nullptr);
// Avoid yielding if the thread isn't even running.
ASSERT(thread->GetStatus() == ThreadStatus::Running);
// Sanity check that the priority is valid
ASSERT(thread->GetPriority() < THREADPRIO_COUNT);
// Yield this thread -- sleep for zero time and force reschedule to different thread
GetCurrentThread()->Sleep(0);
}
void Scheduler::YieldWithLoadBalancing(Thread* thread) {
ASSERT(thread != nullptr);
const auto priority = thread->GetPriority();
const auto core = static_cast<u32>(thread->GetProcessorID());
// Avoid yielding if the thread isn't even running.
ASSERT(thread->GetStatus() == ThreadStatus::Running);
// Sanity check that the priority is valid
ASSERT(priority < THREADPRIO_COUNT);
// Sleep for zero time to be able to force reschedule to different thread
GetCurrentThread()->Sleep(0);
Thread* suggested_thread = nullptr;
// Search through all of the cpu cores (except this one) for a suggested thread.
// Take the first non-nullptr one
for (unsigned cur_core = 0; cur_core < Core::NUM_CPU_CORES; ++cur_core) {
const auto res =
system.CpuCore(cur_core).Scheduler().GetNextSuggestedThread(core, priority);
// If scheduler provides a suggested thread
if (res != nullptr) {
// And its better than the current suggested thread (or is the first valid one)
if (suggested_thread == nullptr ||
suggested_thread->GetPriority() > res->GetPriority()) {
suggested_thread = res;
}
}
}
// If a suggested thread was found, queue that for this core
if (suggested_thread != nullptr)
suggested_thread->ChangeCore(core, suggested_thread->GetAffinityMask());
}
void Scheduler::YieldAndWaitForLoadBalancing(Thread* thread) {
UNIMPLEMENTED_MSG("Wait for load balancing thread yield type is not implemented!");
}
} // namespace Kernel
+162 -111
View File
@@ -20,124 +20,178 @@ namespace Kernel {
class Process;
class Scheduler final {
class GlobalScheduler final {
public:
explicit Scheduler(Core::System& system, Core::ARM_Interface& cpu_core);
~Scheduler();
/// Returns whether there are any threads that are ready to run.
bool HaveReadyThreads() const;
/// Reschedules to the next available thread (call after current thread is suspended)
void Reschedule();
/// Gets the current running thread
Thread* GetCurrentThread() const;
/// Gets the timestamp for the last context switch in ticks.
u64 GetLastContextSwitchTicks() const;
static constexpr u32 NUM_CPU_CORES = 4;
explicit GlobalScheduler(Core::System& system);
~GlobalScheduler();
/// Adds a new thread to the scheduler
void AddThread(SharedPtr<Thread> thread);
/// Removes a thread from the scheduler
void RemoveThread(Thread* thread);
/// Schedules a thread that has become "ready"
void ScheduleThread(Thread* thread, u32 priority);
/// Unschedules a thread that was already scheduled
void UnscheduleThread(Thread* thread, u32 priority);
/// Sets the priority of a thread in the scheduler
void SetThreadPriority(Thread* thread, u32 priority);
/// Gets the next suggested thread for load balancing
Thread* GetNextSuggestedThread(u32 core, u32 minimum_priority) const;
/**
* YieldWithoutLoadBalancing -- analogous to normal yield on a system
* Moves the thread to the end of the ready queue for its priority, and then reschedules the
* system to the new head of the queue.
*
* Example (Single Core -- but can be extrapolated to multi):
* ready_queue[prio=0]: ThreadA, ThreadB, ThreadC (->exec order->)
* Currently Running: ThreadR
*
* ThreadR calls YieldWithoutLoadBalancing
*
* ThreadR is moved to the end of ready_queue[prio=0]:
* ready_queue[prio=0]: ThreadA, ThreadB, ThreadC, ThreadR (->exec order->)
* Currently Running: Nothing
*
* System is rescheduled (ThreadA is popped off of queue):
* ready_queue[prio=0]: ThreadB, ThreadC, ThreadR (->exec order->)
* Currently Running: ThreadA
*
* If the queue is empty at time of call, no yielding occurs. This does not cross between cores
* or priorities at all.
*/
void YieldWithoutLoadBalancing(Thread* thread);
/**
* YieldWithLoadBalancing -- yield but with better selection of the new running thread
* Moves the current thread to the end of the ready queue for its priority, then selects a
* 'suggested thread' (a thread on a different core that could run on this core) from the
* scheduler, changes its core, and reschedules the current core to that thread.
*
* Example (Dual Core -- can be extrapolated to Quad Core, this is just normal yield if it were
* single core):
* ready_queue[core=0][prio=0]: ThreadA, ThreadB (affinities not pictured as irrelevant
* ready_queue[core=1][prio=0]: ThreadC[affinity=both], ThreadD[affinity=core1only]
* Currently Running: ThreadQ on Core 0 || ThreadP on Core 1
*
* ThreadQ calls YieldWithLoadBalancing
*
* ThreadQ is moved to the end of ready_queue[core=0][prio=0]:
* ready_queue[core=0][prio=0]: ThreadA, ThreadB
* ready_queue[core=1][prio=0]: ThreadC[affinity=both], ThreadD[affinity=core1only]
* Currently Running: ThreadQ on Core 0 || ThreadP on Core 1
*
* A list of suggested threads for each core is compiled
* Suggested Threads: {ThreadC on Core 1}
* If this were quad core (as the switch is), there could be between 0 and 3 threads in this
* list. If there are more than one, the thread is selected by highest prio.
*
* ThreadC is core changed to Core 0:
* ready_queue[core=0][prio=0]: ThreadC, ThreadA, ThreadB, ThreadQ
* ready_queue[core=1][prio=0]: ThreadD
* Currently Running: None on Core 0 || ThreadP on Core 1
*
* System is rescheduled (ThreadC is popped off of queue):
* ready_queue[core=0][prio=0]: ThreadA, ThreadB, ThreadQ
* ready_queue[core=1][prio=0]: ThreadD
* Currently Running: ThreadC on Core 0 || ThreadP on Core 1
*
* If no suggested threads can be found this will behave just as normal yield. If there are
* multiple candidates for the suggested thread on a core, the highest prio is taken.
*/
void YieldWithLoadBalancing(Thread* thread);
/// Currently unknown -- asserts as unimplemented on call
void YieldAndWaitForLoadBalancing(Thread* thread);
void RemoveThread(const Thread* thread);
/// Returns a list of all threads managed by the scheduler
const std::vector<SharedPtr<Thread>>& GetThreadList() const {
return thread_list;
}
private:
/**
* Pops and returns the next thread from the thread queue
* @return A pointer to the next ready thread
*/
Thread* PopNextReadyThread();
// Add a thread to the suggested queue of a cpu core. Suggested threads may be
// picked if no thread is scheduled to run on the core.
void Suggest(u32 priority, u32 core, Thread* thread) {
suggested_queue[core].add(thread, priority);
}
// Remove a thread to the suggested queue of a cpu core. Suggested threads may be
// picked if no thread is scheduled to run on the core.
void Unsuggest(u32 priority, u32 core, Thread* thread) {
suggested_queue[core].remove(thread, priority);
}
// Add a thread to the scheduling queue of a cpu core. The thread is added at the
// back the queue in its priority level
void Schedule(u32 priority, u32 core, Thread* thread);
// Add a thread to the scheduling queue of a cpu core. The thread is added at the
// front the queue in its priority level
void SchedulePrepend(u32 priority, u32 core, Thread* thread);
// Reschedule an already scheduled thread based on a new priority
void Reschedule(u32 priority, u32 core, Thread* thread) {
scheduled_queue[core].remove(thread, priority);
scheduled_queue[core].add(thread, priority);
}
// Unschedule a thread.
void Unschedule(u32 priority, u32 core, Thread* thread) {
scheduled_queue[core].remove(thread, priority);
}
// Transfers a thread into an specific core. If the destination_core is -1
// it will be unscheduled from its source code and added into its suggested
// queue.
void TransferToCore(u32 priority, s32 destination_core, Thread* thread) {
const bool schedulable = thread->GetPriority() < THREADPRIO_COUNT;
const s32 source_core = thread->GetProcessorID();
if (source_core == destination_core || !schedulable) {
return;
}
thread->SetProcessorID(destination_core);
if (source_core >= 0) {
Unschedule(priority, source_core, thread);
}
if (destination_core >= 0) {
Unsuggest(priority, destination_core, thread);
Schedule(priority, destination_core, thread);
}
if (source_core >= 0) {
Suggest(priority, source_core, thread);
}
}
/*
* UnloadThread selects a core and forces it to unload its current thread's context
*/
void UnloadThread(s32 core);
/*
* SelectThread takes care of selecting the new scheduled thread.
* It does it in 3 steps:
* - First a thread is selected from the top of the priority queue. If no thread
* is obtained then we move to step two, else we are done.
* - Second we try to get a suggested thread that's not assigned to any core or
* that is not the top thread in that core.
* - Third is no suggested thread is found, we do a second pass and pick a running
* thread in another core and swap it with its current thread.
*/
void SelectThread(u32 core);
bool HaveReadyThreads(u32 core_id) const {
return !scheduled_queue[core_id].empty();
}
/*
* YieldThread takes a thread and moves it to the back of the it's priority list
* This operation can be redundant and no scheduling is changed if marked as so.
*/
void YieldThread(Thread* thread);
/*
* YieldThreadAndBalanceLoad takes a thread and moves it to the back of the it's priority list.
* Afterwards, tries to pick a suggested thread from the suggested queue that has worse time or
* a better priority than the next thread in the core.
* This operation can be redundant and no scheduling is changed if marked as so.
*/
void YieldThreadAndBalanceLoad(Thread* thread);
/*
* YieldThreadAndWaitForLoadBalancing takes a thread and moves it out of the scheduling queue
* and into the suggested queue. If no thread can be squeduled afterwards in that core,
* a suggested thread is obtained instead.
* This operation can be redundant and no scheduling is changed if marked as so.
*/
void YieldThreadAndWaitForLoadBalancing(Thread* thread);
u32 CpuCoresCount() const {
return NUM_CPU_CORES;
}
void SetReselectionPending() {
reselection_pending.store(true, std::memory_order_release);
}
bool IsReselectionPending() const {
return reselection_pending.load();
}
private:
void AskForReselectionOrMarkRedundant(Thread* current_thread, Thread* winner);
static constexpr u32 min_regular_priority = 2;
std::array<Common::MultiLevelQueue<Thread*, THREADPRIO_COUNT>, NUM_CPU_CORES> scheduled_queue;
std::array<Common::MultiLevelQueue<Thread*, THREADPRIO_COUNT>, NUM_CPU_CORES> suggested_queue;
std::atomic<bool> reselection_pending;
/// Lists all thread ids that aren't deleted/etc.
std::vector<SharedPtr<Thread>> thread_list;
Core::System& system;
};
class Scheduler final {
public:
explicit Scheduler(Core::System& system, Core::ARM_Interface& cpu_core, const u32 core_id);
~Scheduler();
/// Returns whether there are any threads that are ready to run.
bool HaveReadyThreads() const;
/// Reschedules to the next available thread (call after current thread is suspended)
void TryDoContextSwitch();
void UnloadThread();
void SelectThreads();
/// Gets the current running thread
Thread* GetCurrentThread() const;
Thread* GetSelectedThread() const;
/// Gets the timestamp for the last context switch in ticks.
u64 GetLastContextSwitchTicks() const;
bool ContextSwitchPending() const {
return context_switch_pending;
}
private:
friend class GlobalScheduler;
/**
* Switches the CPU's active thread context to that of the specified thread
* @param new_thread The thread to switch to
*/
void SwitchContext(Thread* new_thread);
void SwitchContext();
/**
* Called on every context switch to update the internal timestamp
@@ -152,19 +206,16 @@ private:
*/
void UpdateLastContextSwitchTime(Thread* thread, Process* process);
/// Lists all thread ids that aren't deleted/etc.
std::vector<SharedPtr<Thread>> thread_list;
/// Lists only ready thread ids.
Common::MultiLevelQueue<Thread*, THREADPRIO_LOWEST + 1> ready_queue;
SharedPtr<Thread> current_thread = nullptr;
Core::ARM_Interface& cpu_core;
u64 last_context_switch_time = 0;
SharedPtr<Thread> selected_thread = nullptr;
Core::System& system;
static std::mutex scheduler_mutex;
Core::ARM_Interface& cpu_core;
u64 last_context_switch_time = 0;
u64 idle_selection_count = 0;
const u32 core_id;
bool context_switch_pending = false;
};
} // namespace Kernel
+40 -34
View File
@@ -516,7 +516,7 @@ static ResultCode WaitSynchronization(Core::System& system, Handle* index, VAddr
thread->WakeAfterDelay(nano_seconds);
thread->SetWakeupCallback(DefaultThreadWakeupCallback);
system.CpuCore(thread->GetProcessorID()).PrepareReschedule();
system.PrepareReschedule(thread->GetProcessorID());
return RESULT_TIMEOUT;
}
@@ -534,6 +534,7 @@ static ResultCode CancelSynchronization(Core::System& system, Handle thread_hand
}
thread->CancelWait();
system.PrepareReschedule(thread->GetProcessorID());
return RESULT_SUCCESS;
}
@@ -577,7 +578,8 @@ static ResultCode ArbitrateUnlock(Core::System& system, VAddr mutex_addr) {
}
auto* const current_process = system.Kernel().CurrentProcess();
return current_process->GetMutex().Release(mutex_addr);
return current_process->GetMutex().Release(mutex_addr,
system.CurrentScheduler().GetCurrentThread());
}
enum class BreakType : u32 {
@@ -1066,6 +1068,8 @@ static ResultCode SetThreadActivity(Core::System& system, Handle handle, u32 act
}
thread->SetActivity(static_cast<ThreadActivity>(activity));
system.PrepareReschedule(thread->GetProcessorID());
return RESULT_SUCCESS;
}
@@ -1147,7 +1151,7 @@ static ResultCode SetThreadPriority(Core::System& system, Handle handle, u32 pri
thread->SetPriority(priority);
system.CpuCore(thread->GetProcessorID()).PrepareReschedule();
system.PrepareReschedule(thread->GetProcessorID());
return RESULT_SUCCESS;
}
@@ -1503,7 +1507,7 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e
thread->SetName(
fmt::format("thread[entry_point={:X}, handle={:X}]", entry_point, *new_thread_handle));
system.CpuCore(thread->GetProcessorID()).PrepareReschedule();
system.PrepareReschedule(thread->GetProcessorID());
return RESULT_SUCCESS;
}
@@ -1525,7 +1529,7 @@ static ResultCode StartThread(Core::System& system, Handle thread_handle) {
thread->ResumeFromWait();
if (thread->GetStatus() == ThreadStatus::Ready) {
system.CpuCore(thread->GetProcessorID()).PrepareReschedule();
system.PrepareReschedule(thread->GetProcessorID());
}
return RESULT_SUCCESS;
@@ -1537,7 +1541,7 @@ static void ExitThread(Core::System& system) {
auto* const current_thread = system.CurrentScheduler().GetCurrentThread();
current_thread->Stop();
system.CurrentScheduler().RemoveThread(current_thread);
system.GlobalScheduler().RemoveThread(current_thread);
system.PrepareReschedule();
}
@@ -1557,13 +1561,13 @@ static void SleepThread(Core::System& system, s64 nanoseconds) {
if (nanoseconds <= 0) {
switch (static_cast<SleepType>(nanoseconds)) {
case SleepType::YieldWithoutLoadBalancing:
scheduler.YieldWithoutLoadBalancing(current_thread);
current_thread->YieldSimple();
break;
case SleepType::YieldWithLoadBalancing:
scheduler.YieldWithLoadBalancing(current_thread);
current_thread->YieldAndBalanceLoad();
break;
case SleepType::YieldAndWaitForLoadBalancing:
scheduler.YieldAndWaitForLoadBalancing(current_thread);
current_thread->YieldAndWaitForLoadBalancing();
break;
default:
UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds);
@@ -1572,10 +1576,7 @@ static void SleepThread(Core::System& system, s64 nanoseconds) {
current_thread->Sleep(nanoseconds);
}
// Reschedule all CPU cores
for (std::size_t i = 0; i < Core::NUM_CPU_CORES; ++i) {
system.CpuCore(i).PrepareReschedule();
}
system.PrepareReschedule(current_thread->GetProcessorID());
}
/// Wait process wide key atomic
@@ -1606,12 +1607,14 @@ static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr mutex_add
SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
ASSERT(thread);
const auto release_result = current_process->GetMutex().Release(mutex_addr);
SharedPtr<Thread> current_thread = system.CurrentScheduler().GetCurrentThread();
const auto release_result =
current_process->GetMutex().Release(mutex_addr, current_thread.get());
if (release_result.IsError()) {
return release_result;
}
SharedPtr<Thread> current_thread = system.CurrentScheduler().GetCurrentThread();
current_thread->SetCondVarWaitAddress(condition_variable_addr);
current_thread->SetMutexWaitAddress(mutex_addr);
current_thread->SetWaitHandle(thread_handle);
@@ -1622,7 +1625,7 @@ static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr mutex_add
// Note: Deliberately don't attempt to inherit the lock owner's priority.
system.CpuCore(current_thread->GetProcessorID()).PrepareReschedule();
system.PrepareReschedule(current_thread->GetProcessorID());
return RESULT_SUCCESS;
}
@@ -1632,24 +1635,17 @@ static ResultCode SignalProcessWideKey(Core::System& system, VAddr condition_var
LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x{:X}, target=0x{:08X}",
condition_variable_addr, target);
const auto RetrieveWaitingThreads = [&system](std::size_t core_index,
std::vector<SharedPtr<Thread>>& waiting_threads,
VAddr condvar_addr) {
const auto& scheduler = system.Scheduler(core_index);
const auto& thread_list = scheduler.GetThreadList();
for (const auto& thread : thread_list) {
if (thread->GetCondVarWaitAddress() == condvar_addr)
waiting_threads.push_back(thread);
}
};
// Retrieve a list of all threads that are waiting for this condition variable.
std::vector<SharedPtr<Thread>> waiting_threads;
RetrieveWaitingThreads(0, waiting_threads, condition_variable_addr);
RetrieveWaitingThreads(1, waiting_threads, condition_variable_addr);
RetrieveWaitingThreads(2, waiting_threads, condition_variable_addr);
RetrieveWaitingThreads(3, waiting_threads, condition_variable_addr);
const auto& scheduler = system.GlobalScheduler();
const auto& thread_list = scheduler.GetThreadList();
for (const auto& thread : thread_list) {
if (thread->GetCondVarWaitAddress() == condition_variable_addr) {
waiting_threads.push_back(thread);
}
}
// Sort them by priority, such that the highest priority ones come first.
std::sort(waiting_threads.begin(), waiting_threads.end(),
[](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) {
@@ -1704,7 +1700,7 @@ static ResultCode SignalProcessWideKey(Core::System& system, VAddr condition_var
thread->SetLockOwner(nullptr);
thread->SetMutexWaitAddress(0);
thread->SetWaitHandle(0);
system.CpuCore(thread->GetProcessorID()).PrepareReschedule();
system.PrepareReschedule(thread->GetProcessorID());
} else {
// Atomically signal that the mutex now has a waiting thread.
do {
@@ -1728,6 +1724,7 @@ static ResultCode SignalProcessWideKey(Core::System& system, VAddr condition_var
thread->SetStatus(ThreadStatus::WaitMutex);
owner->AddMutexWaiter(thread);
system.PrepareReschedule(thread->GetProcessorID());
}
}
@@ -1754,7 +1751,12 @@ static ResultCode WaitForAddress(Core::System& system, VAddr address, u32 type,
const auto arbitration_type = static_cast<AddressArbiter::ArbitrationType>(type);
auto& address_arbiter = system.Kernel().CurrentProcess()->GetAddressArbiter();
return address_arbiter.WaitForAddress(address, arbitration_type, value, timeout);
const ResultCode result =
address_arbiter.WaitForAddress(address, arbitration_type, value, timeout);
if (result == RESULT_SUCCESS) {
system.PrepareReschedule();
}
return result;
}
// Signals to an address (via Address Arbiter)
@@ -2040,7 +2042,10 @@ static ResultCode SetThreadCoreMask(Core::System& system, Handle thread_handle,
return ERR_INVALID_HANDLE;
}
system.PrepareReschedule(thread->GetProcessorID());
thread->ChangeCore(core, affinity_mask);
system.PrepareReschedule(thread->GetProcessorID());
return RESULT_SUCCESS;
}
@@ -2151,6 +2156,7 @@ static ResultCode SignalEvent(Core::System& system, Handle handle) {
}
writable_event->Signal();
system.PrepareReschedule();
return RESULT_SUCCESS;
}
+189 -59
View File
@@ -45,15 +45,7 @@ void Thread::Stop() {
callback_handle);
kernel.ThreadWakeupCallbackHandleTable().Close(callback_handle);
callback_handle = 0;
// Clean up thread from ready queue
// This is only needed when the thread is terminated forcefully (SVC TerminateProcess)
if (status == ThreadStatus::Ready || status == ThreadStatus::Paused) {
scheduler->UnscheduleThread(this, current_priority);
}
status = ThreadStatus::Dead;
SetStatus(ThreadStatus::Dead);
WakeupAllWaitingThreads();
// Clean up any dangling references in objects that this thread was waiting for
@@ -132,13 +124,11 @@ void Thread::ResumeFromWait() {
wakeup_callback = nullptr;
if (activity == ThreadActivity::Paused) {
status = ThreadStatus::Paused;
SetStatus(ThreadStatus::Paused);
return;
}
status = ThreadStatus::Ready;
ChangeScheduler();
SetStatus(ThreadStatus::Ready);
}
void Thread::CancelWait() {
@@ -205,9 +195,9 @@ 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;
auto& scheduler = kernel.GlobalScheduler();
scheduler.AddThread(thread);
thread->tls_address = thread->owner_process->CreateTLSRegion();
thread->scheduler = &system.Scheduler(processor_id);
thread->scheduler->AddThread(thread);
thread->owner_process->RegisterThread(thread.get());
@@ -250,6 +240,22 @@ void Thread::SetStatus(ThreadStatus new_status) {
return;
}
switch (new_status) {
case ThreadStatus::Ready:
case ThreadStatus::Running:
SetSchedulingStatus(ThreadSchedStatus::Runnable);
break;
case ThreadStatus::Dormant:
SetSchedulingStatus(ThreadSchedStatus::None);
break;
case ThreadStatus::Dead:
SetSchedulingStatus(ThreadSchedStatus::Exited);
break;
default:
SetSchedulingStatus(ThreadSchedStatus::Paused);
break;
}
if (status == ThreadStatus::Running) {
last_running_ticks = Core::System::GetInstance().CoreTiming().GetTicks();
}
@@ -311,8 +317,7 @@ void Thread::UpdatePriority() {
return;
}
scheduler->SetThreadPriority(this, new_priority);
current_priority = new_priority;
SetCurrentPriority(new_priority);
if (!lock_owner) {
return;
@@ -328,47 +333,7 @@ void Thread::UpdatePriority() {
}
void Thread::ChangeCore(u32 core, u64 mask) {
ideal_core = core;
affinity_mask = mask;
ChangeScheduler();
}
void Thread::ChangeScheduler() {
if (status != ThreadStatus::Ready) {
return;
}
auto& system = Core::System::GetInstance();
std::optional<s32> new_processor_id{GetNextProcessorId(affinity_mask)};
if (!new_processor_id) {
new_processor_id = processor_id;
}
if (ideal_core != -1 && system.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 = system.Scheduler(*new_processor_id);
if (*new_processor_id != processor_id) {
// Remove thread from previous core's scheduler
scheduler->RemoveThread(this);
next_scheduler.AddThread(this);
}
processor_id = *new_processor_id;
// If the thread was ready, unschedule from the previous core and schedule on the new core
scheduler->UnscheduleThread(this, current_priority);
next_scheduler.ScheduleThread(this, current_priority);
// Change thread's scheduler
scheduler = &next_scheduler;
system.CpuCore(processor_id).PrepareReschedule();
SetCoreAndAffinityMask(core, mask);
}
bool Thread::AllWaitObjectsReady() const {
@@ -391,7 +356,7 @@ void Thread::SetActivity(ThreadActivity value) {
if (status == ThreadStatus::Ready) {
status = ThreadStatus::Paused;
} else if (status == ThreadStatus::Running) {
status = ThreadStatus::Paused;
SetStatus(ThreadStatus::Paused);
Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
}
} else if (status == ThreadStatus::Paused) {
@@ -408,6 +373,171 @@ void Thread::Sleep(s64 nanoseconds) {
WakeAfterDelay(nanoseconds);
}
void Thread::YieldSimple() {
auto& scheduler = kernel.GlobalScheduler();
scheduler.YieldThread(this);
}
void Thread::YieldAndBalanceLoad() {
auto& scheduler = kernel.GlobalScheduler();
scheduler.YieldThreadAndBalanceLoad(this);
}
void Thread::YieldAndWaitForLoadBalancing() {
auto& scheduler = kernel.GlobalScheduler();
scheduler.YieldThreadAndWaitForLoadBalancing(this);
}
void Thread::SetSchedulingStatus(ThreadSchedStatus new_status) {
const u32 old_flags = scheduling_state;
scheduling_state =
(scheduling_state & ThreadSchedMasks::HighMask) | static_cast<u32>(new_status);
AdjustSchedulingOnStatus(old_flags);
}
void Thread::SetCurrentPriority(u32 new_priority) {
u32 old_priority = std::exchange(current_priority, new_priority);
AdjustSchedulingOnPriority(old_priority);
}
ResultCode Thread::SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask) {
const auto HighestSetCore = [](u64 mask, u32 max_cores) {
for (s32 core = max_cores - 1; core >= 0; core--) {
if (((mask >> core) & 1) != 0) {
return core;
}
}
return -1;
};
const bool use_override = affinity_override_count != 0;
// The value -3 is "do not change the ideal core".
if (new_core == -3) {
new_core = use_override ? ideal_core_override : ideal_core;
if ((new_affinity_mask & (1 << new_core)) == 0) {
return ERR_INVALID_COMBINATION;
}
}
if (use_override) {
ideal_core_override = new_core;
affinity_mask_override = new_affinity_mask;
} else {
const u64 old_affinity_mask = std::exchange(affinity_mask, new_affinity_mask);
ideal_core = new_core;
if (old_affinity_mask != new_affinity_mask) {
const s32 old_core = processor_id;
if (processor_id >= 0 && ((affinity_mask >> processor_id) & 1) == 0) {
if (ideal_core < 0) {
processor_id = HighestSetCore(affinity_mask, GlobalScheduler::NUM_CPU_CORES);
} else {
processor_id = ideal_core;
}
}
AdjustSchedulingOnAffinity(old_affinity_mask, old_core);
}
}
return RESULT_SUCCESS;
}
void Thread::AdjustSchedulingOnStatus(u32 old_flags) {
if (old_flags == scheduling_state) {
return;
}
auto& scheduler = kernel.GlobalScheduler();
if (static_cast<ThreadSchedStatus>(old_flags & ThreadSchedMasks::LowMask) ==
ThreadSchedStatus::Runnable) {
// In this case the thread was running, now it's pausing/exitting
if (processor_id >= 0) {
scheduler.Unschedule(current_priority, processor_id, this);
}
for (u32 core = 0; core < GlobalScheduler::NUM_CPU_CORES; core++) {
if (core != processor_id && ((affinity_mask >> core) & 1) != 0) {
scheduler.Unsuggest(current_priority, core, this);
}
}
} else if (GetSchedulingStatus() == ThreadSchedStatus::Runnable) {
// The thread is now set to running from being stopped
if (processor_id >= 0) {
scheduler.Schedule(current_priority, processor_id, this);
}
for (u32 core = 0; core < GlobalScheduler::NUM_CPU_CORES; core++) {
if (core != processor_id && ((affinity_mask >> core) & 1) != 0) {
scheduler.Suggest(current_priority, core, this);
}
}
}
scheduler.SetReselectionPending();
}
void Thread::AdjustSchedulingOnPriority(u32 old_priority) {
if (GetSchedulingStatus() != ThreadSchedStatus::Runnable) {
return;
}
auto& scheduler = Core::System::GetInstance().GlobalScheduler();
if (processor_id >= 0) {
scheduler.Unschedule(old_priority, processor_id, this);
}
for (u32 core = 0; core < GlobalScheduler::NUM_CPU_CORES; core++) {
if (core != processor_id && ((affinity_mask >> core) & 1) != 0) {
scheduler.Unsuggest(old_priority, core, this);
}
}
// Add thread to the new priority queues.
Thread* current_thread = GetCurrentThread();
if (processor_id >= 0) {
if (current_thread == this) {
scheduler.SchedulePrepend(current_priority, processor_id, this);
} else {
scheduler.Schedule(current_priority, processor_id, this);
}
}
for (u32 core = 0; core < GlobalScheduler::NUM_CPU_CORES; core++) {
if (core != processor_id && ((affinity_mask >> core) & 1) != 0) {
scheduler.Suggest(current_priority, core, this);
}
}
scheduler.SetReselectionPending();
}
void Thread::AdjustSchedulingOnAffinity(u64 old_affinity_mask, s32 old_core) {
auto& scheduler = Core::System::GetInstance().GlobalScheduler();
if (GetSchedulingStatus() != ThreadSchedStatus::Runnable ||
current_priority >= THREADPRIO_COUNT) {
return;
}
for (u32 core = 0; core < GlobalScheduler::NUM_CPU_CORES; core++) {
if (((old_affinity_mask >> core) & 1) != 0) {
if (core == old_core) {
scheduler.Unschedule(current_priority, core, this);
} else {
scheduler.Unsuggest(current_priority, core, this);
}
}
}
for (u32 core = 0; core < GlobalScheduler::NUM_CPU_CORES; core++) {
if (((affinity_mask >> core) & 1) != 0) {
if (core == processor_id) {
scheduler.Schedule(current_priority, core, this);
} else {
scheduler.Suggest(current_priority, core, this);
}
}
}
scheduler.SetReselectionPending();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/**
+59 -1
View File
@@ -75,6 +75,26 @@ enum class ThreadActivity : u32 {
Paused = 1,
};
enum class ThreadSchedStatus : u32 {
None = 0,
Paused = 1,
Runnable = 2,
Exited = 3,
};
enum ThreadSchedFlags : u32 {
ProcessPauseFlag = 1 << 4,
ThreadPauseFlag = 1 << 5,
ProcessDebugPauseFlag = 1 << 6,
KernelInitPauseFlag = 1 << 8,
};
enum ThreadSchedMasks : u32 {
LowMask = 0x000f,
HighMask = 0xfff0,
ForcePauseMask = 0x0070,
};
class Thread final : public WaitObject {
public:
using MutexWaitingThreads = std::vector<SharedPtr<Thread>>;
@@ -278,6 +298,10 @@ public:
return processor_id;
}
void SetProcessorID(s32 new_core) {
processor_id = new_core;
}
Process* GetOwnerProcess() {
return owner_process;
}
@@ -383,11 +407,38 @@ public:
/// Sleeps this thread for the given amount of nanoseconds.
void Sleep(s64 nanoseconds);
/// Yields this thread without rebalancing loads.
void YieldSimple();
/// Yields this thread and does a load rebalancing.
void YieldAndBalanceLoad();
/// Yields this thread and if the core is left idle, loads are rebalanced
void YieldAndWaitForLoadBalancing();
ThreadSchedStatus GetSchedulingStatus() const {
return static_cast<ThreadSchedStatus>(scheduling_state & ThreadSchedMasks::LowMask);
}
bool IsRunning() const {
return is_running;
}
void SetIsRunning(bool value) {
is_running = value;
}
private:
explicit Thread(KernelCore& kernel);
~Thread() override;
void ChangeScheduler();
void SetSchedulingStatus(ThreadSchedStatus new_status);
void SetCurrentPriority(u32 new_priority);
ResultCode SetCoreAndAffinityMask(s32 new_core, u64 new_affinity_mask);
void AdjustSchedulingOnStatus(u32 old_flags);
void AdjustSchedulingOnPriority(u32 old_priority);
void AdjustSchedulingOnAffinity(u64 old_affinity_mask, s32 old_core);
Core::ARM_Interface::ThreadContext context{};
@@ -453,6 +504,13 @@ private:
ThreadActivity activity = ThreadActivity::Normal;
s32 ideal_core_override = -1;
u64 affinity_mask_override = 0x1;
u32 affinity_override_count = 0;
u32 scheduling_state = 0;
bool is_running = false;
std::string name;
};
+2
View File
@@ -23,6 +23,8 @@ SharedPtr<TransferMemory> TransferMemory::Create(KernelCore& kernel, VAddr base_
transfer_memory->owner_permissions = permissions;
transfer_memory->owner_process = kernel.CurrentProcess();
transfer_memory->MapMemory(base_address, size, permissions);
return transfer_memory;
}
+37 -35
View File
@@ -296,12 +296,6 @@ ResultVal<VAddr> VMManager::SetHeapSize(u64 size) {
}
ResultCode VMManager::MapPhysicalMemory(VAddr target, u64 size) {
const auto end_addr = target + size;
const auto last_addr = end_addr - 1;
VAddr cur_addr = target;
ResultCode result = RESULT_SUCCESS;
// Check how much memory we've already mapped.
const auto mapped_size_result = SizeOfAllocatedVMAsInRange(target, size);
if (mapped_size_result.Failed()) {
@@ -324,13 +318,16 @@ ResultCode VMManager::MapPhysicalMemory(VAddr target, u64 size) {
// Keep track of the memory regions we unmap.
std::vector<std::pair<u64, u64>> mapped_regions;
ResultCode result = RESULT_SUCCESS;
// Iterate, trying to map memory.
{
cur_addr = target;
const auto end_addr = target + size;
const auto last_addr = end_addr - 1;
VAddr cur_addr = target;
auto iter = FindVMA(target);
ASSERT_MSG(iter != vma_map.end(), "MapPhysicalMemory iter != end");
ASSERT(iter != vma_map.end());
while (true) {
const auto& vma = iter->second;
@@ -342,7 +339,7 @@ ResultCode VMManager::MapPhysicalMemory(VAddr target, u64 size) {
const auto map_size = std::min(end_addr - cur_addr, vma_end - cur_addr);
if (vma.state == MemoryState::Unmapped) {
const auto map_res =
MapMemoryBlock(cur_addr, std::make_shared<PhysicalMemory>(map_size, 0), 0,
MapMemoryBlock(cur_addr, std::make_shared<PhysicalMemory>(map_size), 0,
map_size, MemoryState::Heap, VMAPermission::ReadWrite);
result = map_res.Code();
if (result.IsError()) {
@@ -360,7 +357,7 @@ ResultCode VMManager::MapPhysicalMemory(VAddr target, u64 size) {
// Advance to the next block.
cur_addr = vma_end;
iter = FindVMA(cur_addr);
ASSERT_MSG(iter != vma_map.end(), "MapPhysicalMemory iter != end");
ASSERT(iter != vma_map.end());
}
}
@@ -368,7 +365,7 @@ ResultCode VMManager::MapPhysicalMemory(VAddr target, u64 size) {
if (result.IsError()) {
for (const auto [unmap_address, unmap_size] : mapped_regions) {
ASSERT_MSG(UnmapRange(unmap_address, unmap_size).IsSuccess(),
"MapPhysicalMemory un-map on error");
"Failed to unmap memory range.");
}
return result;
@@ -381,12 +378,6 @@ ResultCode VMManager::MapPhysicalMemory(VAddr target, u64 size) {
}
ResultCode VMManager::UnmapPhysicalMemory(VAddr target, u64 size) {
const auto end_addr = target + size;
const auto last_addr = end_addr - 1;
VAddr cur_addr = target;
ResultCode result = RESULT_SUCCESS;
// Check how much memory is currently mapped.
const auto mapped_size_result = SizeOfUnmappablePhysicalMemoryInRange(target, size);
if (mapped_size_result.Failed()) {
@@ -401,13 +392,16 @@ ResultCode VMManager::UnmapPhysicalMemory(VAddr target, u64 size) {
// Keep track of the memory regions we unmap.
std::vector<std::pair<u64, u64>> unmapped_regions;
ResultCode result = RESULT_SUCCESS;
// Try to unmap regions.
{
cur_addr = target;
const auto end_addr = target + size;
const auto last_addr = end_addr - 1;
VAddr cur_addr = target;
auto iter = FindVMA(target);
ASSERT_MSG(iter != vma_map.end(), "UnmapPhysicalMemory iter != end");
ASSERT(iter != vma_map.end());
while (true) {
const auto& vma = iter->second;
@@ -434,7 +428,7 @@ ResultCode VMManager::UnmapPhysicalMemory(VAddr target, u64 size) {
// Advance to the next block.
cur_addr = vma_end;
iter = FindVMA(cur_addr);
ASSERT_MSG(iter != vma_map.end(), "UnmapPhysicalMemory iter != end");
ASSERT(iter != vma_map.end());
}
}
@@ -443,10 +437,12 @@ ResultCode VMManager::UnmapPhysicalMemory(VAddr target, u64 size) {
if (result.IsError()) {
for (const auto [map_address, map_size] : unmapped_regions) {
const auto remap_res =
MapMemoryBlock(map_address, std::make_shared<PhysicalMemory>(map_size, 0), 0,
map_size, MemoryState::Heap, VMAPermission::None);
ASSERT_MSG(remap_res.Succeeded(), "UnmapPhysicalMemory re-map on error");
MapMemoryBlock(map_address, std::make_shared<PhysicalMemory>(map_size), 0, map_size,
MemoryState::Heap, VMAPermission::None);
ASSERT_MSG(remap_res.Succeeded(), "Failed to remap a memory block.");
}
return result;
}
// Update mapped amount
@@ -757,20 +753,26 @@ void VMManager::MergeAdjacentVMA(VirtualMemoryArea& left, const VirtualMemoryAre
// Always merge allocated memory blocks, even when they don't share the same backing block.
if (left.type == VMAType::AllocatedMemoryBlock &&
(left.backing_block != right.backing_block || left.offset + left.size != right.offset)) {
const auto right_begin = right.backing_block->begin() + right.offset;
const auto right_end = right_begin + right.size;
// Check if we can save work.
if (left.offset == 0 && left.size == left.backing_block->size()) {
// Fast case: left is an entire backing block.
left.backing_block->insert(left.backing_block->end(),
right.backing_block->begin() + right.offset,
right.backing_block->begin() + right.offset + right.size);
left.backing_block->insert(left.backing_block->end(), right_begin, right_end);
} else {
// Slow case: make a new memory block for left and right.
const auto left_begin = left.backing_block->begin() + left.offset;
const auto left_end = left_begin + left.size;
const auto left_size = static_cast<std::size_t>(std::distance(left_begin, left_end));
const auto right_size = static_cast<std::size_t>(std::distance(right_begin, right_end));
auto new_memory = std::make_shared<PhysicalMemory>();
new_memory->insert(new_memory->end(), left.backing_block->begin() + left.offset,
left.backing_block->begin() + left.offset + left.size);
new_memory->insert(new_memory->end(), right.backing_block->begin() + right.offset,
right.backing_block->begin() + right.offset + right.size);
left.backing_block = new_memory;
new_memory->reserve(left_size + right_size);
new_memory->insert(new_memory->end(), left_begin, left_end);
new_memory->insert(new_memory->end(), right_begin, right_end);
left.backing_block = std::move(new_memory);
left.offset = 0;
}
@@ -965,7 +967,7 @@ ResultVal<std::size_t> VMManager::SizeOfAllocatedVMAsInRange(VAddr address,
VAddr cur_addr = address;
auto iter = FindVMA(cur_addr);
ASSERT_MSG(iter != vma_map.end(), "SizeOfAllocatedVMAsInRange iter != end");
ASSERT(iter != vma_map.end());
while (true) {
const auto& vma = iter->second;
@@ -986,7 +988,7 @@ ResultVal<std::size_t> VMManager::SizeOfAllocatedVMAsInRange(VAddr address,
// Advance to the next block.
cur_addr = vma_end;
iter = std::next(iter);
ASSERT_MSG(iter != vma_map.end(), "SizeOfAllocatedVMAsInRange iter != end");
ASSERT(iter != vma_map.end());
}
return MakeResult(mapped_size);
@@ -1000,7 +1002,7 @@ ResultVal<std::size_t> VMManager::SizeOfUnmappablePhysicalMemoryInRange(VAddr ad
VAddr cur_addr = address;
auto iter = FindVMA(cur_addr);
ASSERT_MSG(iter != vma_map.end(), "SizeOfUnmappablePhysicalMemoryInRange iter != end");
ASSERT(iter != vma_map.end());
while (true) {
const auto& vma = iter->second;
@@ -1029,7 +1031,7 @@ ResultVal<std::size_t> VMManager::SizeOfUnmappablePhysicalMemoryInRange(VAddr ad
// Advance to the next block.
cur_addr = vma_end;
iter = std::next(iter);
ASSERT_MSG(iter != vma_map.end(), "SizeOfUnmappablePhysicalMemoryInRange iter != end");
ASSERT(iter != vma_map.end());
}
return MakeResult(mapped_size);
+4 -4
View File
@@ -454,8 +454,8 @@ public:
/// Maps memory at a given address.
///
/// @param addr The virtual address to map memory at.
/// @param size The amount of memory to map.
/// @param target The virtual address to map memory at.
/// @param size The amount of memory to map.
///
/// @note The destination address must lie within the Map region.
///
@@ -468,8 +468,8 @@ public:
/// Unmaps memory at a given address.
///
/// @param addr The virtual address to unmap memory at.
/// @param size The amount of memory to unmap.
/// @param target The virtual address to unmap memory at.
/// @param size The amount of memory to unmap.
///
/// @note The destination address must lie within the Map region.
///
+5 -11
View File
@@ -6,6 +6,8 @@
#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/core_cpu.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/thread.h"
@@ -48,17 +50,8 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() const {
if (ShouldWait(thread.get()))
continue;
// A thread is ready to run if it's either in ThreadStatus::WaitSynch
// and the rest of the objects it is waiting on are ready.
bool ready_to_run = true;
if (thread_status == ThreadStatus::WaitSynch) {
ready_to_run = thread->AllWaitObjectsReady();
}
if (ready_to_run) {
candidate = thread.get();
candidate_priority = thread->GetPriority();
}
candidate = thread.get();
candidate_priority = thread->GetPriority();
}
return candidate;
@@ -95,6 +88,7 @@ void WaitObject::WakeupWaitingThread(SharedPtr<Thread> thread) {
}
if (resume) {
thread->ResumeFromWait();
Core::System::GetInstance().PrepareReschedule(thread->GetProcessorID());
}
}
+122 -16
View File
@@ -31,6 +31,9 @@
namespace Service::Account {
constexpr ResultCode ERR_INVALID_BUFFER_SIZE{ErrorModule::Account, 30};
constexpr ResultCode ERR_FAILED_SAVE_DATA{ErrorModule::Account, 100};
static std::string GetImagePath(Common::UUID uuid) {
return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
"/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg";
@@ -41,22 +44,33 @@ static constexpr u32 SanitizeJPEGSize(std::size_t size) {
return static_cast<u32>(std::min(size, max_jpeg_image_size));
}
class IProfile final : public ServiceFramework<IProfile> {
class IProfileCommon : public ServiceFramework<IProfileCommon> {
public:
explicit IProfile(Common::UUID user_id, ProfileManager& profile_manager)
: ServiceFramework("IProfile"), profile_manager(profile_manager), user_id(user_id) {
explicit IProfileCommon(const char* name, bool editor_commands, Common::UUID user_id,
ProfileManager& profile_manager)
: ServiceFramework(name), profile_manager(profile_manager), user_id(user_id) {
static const FunctionInfo functions[] = {
{0, &IProfile::Get, "Get"},
{1, &IProfile::GetBase, "GetBase"},
{10, &IProfile::GetImageSize, "GetImageSize"},
{11, &IProfile::LoadImage, "LoadImage"},
{0, &IProfileCommon::Get, "Get"},
{1, &IProfileCommon::GetBase, "GetBase"},
{10, &IProfileCommon::GetImageSize, "GetImageSize"},
{11, &IProfileCommon::LoadImage, "LoadImage"},
};
RegisterHandlers(functions);
if (editor_commands) {
static const FunctionInfo editor_functions[] = {
{100, &IProfileCommon::Store, "Store"},
{101, &IProfileCommon::StoreWithImage, "StoreWithImage"},
};
RegisterHandlers(editor_functions);
}
}
private:
protected:
void Get(Kernel::HLERequestContext& ctx) {
LOG_INFO(Service_ACC, "called user_id={}", user_id.Format());
LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
ProfileBase profile_base{};
ProfileData data{};
if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) {
@@ -75,7 +89,7 @@ private:
}
void GetBase(Kernel::HLERequestContext& ctx) {
LOG_INFO(Service_ACC, "called user_id={}", user_id.Format());
LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
ProfileBase profile_base{};
if (profile_manager.GetProfileBase(user_id, profile_base)) {
IPC::ResponseBuilder rb{ctx, 16};
@@ -127,10 +141,91 @@ private:
}
}
const ProfileManager& profile_manager;
void Store(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto base = rp.PopRaw<ProfileBase>();
const auto user_data = ctx.ReadBuffer();
LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid={}",
Common::StringFromFixedZeroTerminatedBuffer(
reinterpret_cast<const char*>(base.username.data()), base.username.size()),
base.timestamp, base.user_uuid.Format());
if (user_data.size() < sizeof(ProfileData)) {
LOG_ERROR(Service_ACC, "ProfileData buffer too small!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_INVALID_BUFFER_SIZE);
return;
}
ProfileData data;
std::memcpy(&data, user_data.data(), sizeof(ProfileData));
if (!profile_manager.SetProfileBaseAndData(user_id, base, data)) {
LOG_ERROR(Service_ACC, "Failed to update profile data and base!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_FAILED_SAVE_DATA);
return;
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void StoreWithImage(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto base = rp.PopRaw<ProfileBase>();
const auto user_data = ctx.ReadBuffer();
const auto image_data = ctx.ReadBuffer(1);
LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid={}",
Common::StringFromFixedZeroTerminatedBuffer(
reinterpret_cast<const char*>(base.username.data()), base.username.size()),
base.timestamp, base.user_uuid.Format());
if (user_data.size() < sizeof(ProfileData)) {
LOG_ERROR(Service_ACC, "ProfileData buffer too small!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_INVALID_BUFFER_SIZE);
return;
}
ProfileData data;
std::memcpy(&data, user_data.data(), sizeof(ProfileData));
FileUtil::IOFile image(GetImagePath(user_id), "wb");
if (!image.IsOpen() || !image.Resize(image_data.size()) ||
image.WriteBytes(image_data.data(), image_data.size()) != image_data.size() ||
!profile_manager.SetProfileBaseAndData(user_id, base, data)) {
LOG_ERROR(Service_ACC, "Failed to update profile data, base, and image!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_FAILED_SAVE_DATA);
return;
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
ProfileManager& profile_manager;
Common::UUID user_id; ///< The user id this profile refers to.
};
class IProfile final : public IProfileCommon {
public:
IProfile(Common::UUID user_id, ProfileManager& profile_manager)
: IProfileCommon("IProfile", false, user_id, profile_manager) {}
};
class IProfileEditor final : public IProfileCommon {
public:
IProfileEditor(Common::UUID user_id, ProfileManager& profile_manager)
: IProfileCommon("IProfileEditor", true, user_id, profile_manager) {}
};
class IManagerForApplication final : public ServiceFramework<IManagerForApplication> {
public:
IManagerForApplication() : ServiceFramework("IManagerForApplication") {
@@ -168,7 +263,7 @@ private:
};
void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) {
LOG_INFO(Service_ACC, "called");
LOG_DEBUG(Service_ACC, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(static_cast<u32>(profile_manager->GetUserCount()));
@@ -177,7 +272,7 @@ void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) {
void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
Common::UUID user_id = rp.PopRaw<Common::UUID>();
LOG_INFO(Service_ACC, "called user_id={}", user_id.Format());
LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
@@ -185,21 +280,21 @@ void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) {
}
void Module::Interface::ListAllUsers(Kernel::HLERequestContext& ctx) {
LOG_INFO(Service_ACC, "called");
LOG_DEBUG(Service_ACC, "called");
ctx.WriteBuffer(profile_manager->GetAllUsers());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void Module::Interface::ListOpenUsers(Kernel::HLERequestContext& ctx) {
LOG_INFO(Service_ACC, "called");
LOG_DEBUG(Service_ACC, "called");
ctx.WriteBuffer(profile_manager->GetOpenUsers());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void Module::Interface::GetLastOpenedUser(Kernel::HLERequestContext& ctx) {
LOG_INFO(Service_ACC, "called");
LOG_DEBUG(Service_ACC, "called");
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<Common::UUID>(profile_manager->GetLastOpenedUser());
@@ -322,6 +417,17 @@ void Module::Interface::IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx
rb.Push(is_locked);
}
void Module::Interface::GetProfileEditor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
Common::UUID user_id = rp.PopRaw<Common::UUID>();
LOG_DEBUG(Service_ACC, "called, user_id={}", user_id.Format());
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IProfileEditor>(user_id, *profile_manager);
}
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
+1
View File
@@ -32,6 +32,7 @@ public:
void IsUserRegistrationRequestPermitted(Kernel::HLERequestContext& ctx);
void TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx);
void IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx);
void GetProfileEditor(Kernel::HLERequestContext& ctx);
private:
ResultCode InitializeApplicationInfoBase(u64 process_id);
+1 -1
View File
@@ -41,7 +41,7 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
{202, nullptr, "CancelUserRegistration"},
{203, nullptr, "DeleteUser"},
{204, nullptr, "SetUserPosition"},
{205, nullptr, "GetProfileEditor"},
{205, &ACC_SU::GetProfileEditor, "GetProfileEditor"},
{206, nullptr, "CompleteUserRegistrationForcibly"},
{210, nullptr, "CreateFloatingRegistrationRequest"},
{230, nullptr, "AuthenticateServiceAsync"},
@@ -305,6 +305,17 @@ bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) {
return true;
}
bool ProfileManager::SetProfileBaseAndData(Common::UUID uuid, const ProfileBase& profile_new,
const ProfileData& data_new) {
const auto index = GetUserIndex(uuid);
if (index.has_value() && SetProfileBase(uuid, profile_new)) {
profiles[*index].data = data_new;
return true;
}
return false;
}
void ProfileManager::ParseUserSaveFile() {
FileUtil::IOFile save(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat",
@@ -91,6 +91,8 @@ public:
bool RemoveUser(Common::UUID uuid);
bool SetProfileBase(Common::UUID uuid, const ProfileBase& profile_new);
bool SetProfileBaseAndData(Common::UUID uuid, const ProfileBase& profile_new,
const ProfileData& data_new);
private:
void ParseUserSaveFile();
+111 -52
View File
@@ -31,6 +31,7 @@
#include "core/hle/service/am/tcap.h"
#include "core/hle/service/apm/controller.h"
#include "core/hle/service/apm/interface.h"
#include "core/hle/service/bcat/backend/backend.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/ns/ns.h"
#include "core/hle/service/nvflinger/nvflinger.h"
@@ -46,17 +47,23 @@ constexpr ResultCode ERR_NO_DATA_IN_CHANNEL{ErrorModule::AM, 0x2};
constexpr ResultCode ERR_NO_MESSAGES{ErrorModule::AM, 0x3};
constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7};
constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA;
enum class LaunchParameterKind : u32 {
ApplicationSpecific = 1,
AccountPreselectedUser = 2,
};
struct LaunchParameters {
constexpr u32 LAUNCH_PARAMETER_ACCOUNT_PRESELECTED_USER_MAGIC = 0xC79497CA;
struct LaunchParameterAccountPreselectedUser {
u32_le magic;
u32_le is_account_selected;
u128 current_user;
INSERT_PADDING_BYTES(0x70);
};
static_assert(sizeof(LaunchParameters) == 0x88);
static_assert(sizeof(LaunchParameterAccountPreselectedUser) == 0x88);
IWindowController::IWindowController() : ServiceFramework("IWindowController") {
IWindowController::IWindowController(Core::System& system_)
: ServiceFramework("IWindowController"), system{system_} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "CreateWindow"},
@@ -75,7 +82,7 @@ IWindowController::IWindowController() : ServiceFramework("IWindowController") {
IWindowController::~IWindowController() = default;
void IWindowController::GetAppletResourceUserId(Kernel::HLERequestContext& ctx) {
const u64 process_id = Core::System::GetInstance().Kernel().CurrentProcess()->GetProcessID();
const u64 process_id = system.CurrentProcess()->GetProcessID();
LOG_DEBUG(Service_AM, "called. Process ID=0x{:016X}", process_id);
@@ -231,11 +238,12 @@ IDebugFunctions::IDebugFunctions() : ServiceFramework{"IDebugFunctions"} {
IDebugFunctions::~IDebugFunctions() = default;
ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
: ServiceFramework("ISelfController"), nvflinger(std::move(nvflinger)) {
ISelfController::ISelfController(Core::System& system,
std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
: ServiceFramework("ISelfController"), system(system), nvflinger(std::move(nvflinger)) {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "Exit"},
{0, &ISelfController::Exit, "Exit"},
{1, &ISelfController::LockExit, "LockExit"},
{2, &ISelfController::UnlockExit, "UnlockExit"},
{3, &ISelfController::EnterFatalSection, "EnterFatalSection"},
@@ -280,7 +288,7 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
auto& kernel = system.Kernel();
launchable_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
"ISelfController:LaunchableEvent");
@@ -296,15 +304,28 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
ISelfController::~ISelfController() = default;
void ISelfController::Exit(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
system.Shutdown();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void ISelfController::LockExit(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
LOG_DEBUG(Service_AM, "called");
system.SetExitLock(true);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void ISelfController::UnlockExit(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
LOG_DEBUG(Service_AM, "called");
system.SetExitLock(false);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -501,8 +522,7 @@ void ISelfController::GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequest
rb.PushCopyObjects(accumulated_suspended_tick_changed_event.readable);
}
AppletMessageQueue::AppletMessageQueue() {
auto& kernel = Core::System::GetInstance().Kernel();
AppletMessageQueue::AppletMessageQueue(Kernel::KernelCore& kernel) {
on_new_message = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
"AMMessageQueue:OnMessageRecieved");
on_operation_mode_changed = Kernel::WritableEvent::CreateEventPair(
@@ -549,6 +569,10 @@ void AppletMessageQueue::OperationModeChanged() {
on_operation_mode_changed.writable->Signal();
}
void AppletMessageQueue::RequestExit() {
PushMessage(AppletMessage::ExitRequested);
}
ICommonStateGetter::ICommonStateGetter(Core::System& system,
std::shared_ptr<AppletMessageQueue> msg_queue)
: ServiceFramework("ICommonStateGetter"), system(system), msg_queue(std::move(msg_queue)) {
@@ -823,17 +847,16 @@ private:
void PopInteractiveOutData(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
const auto storage = applet->GetBroker().PopInteractiveDataToGame();
if (storage == nullptr) {
LOG_ERROR(Service_AM,
"storage is a nullptr. There is no data in the current interactive channel");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_NO_DATA_IN_CHANNEL);
return;
}
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IStorage>(std::move(*storage));
}
@@ -937,9 +960,8 @@ void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) {
rb.Push(RESULT_SUCCESS);
}
ILibraryAppletCreator::ILibraryAppletCreator(u64 current_process_title_id)
: ServiceFramework("ILibraryAppletCreator"),
current_process_title_id(current_process_title_id) {
ILibraryAppletCreator::ILibraryAppletCreator(Core::System& system_)
: ServiceFramework("ILibraryAppletCreator"), system{system_} {
static const FunctionInfo functions[] = {
{0, &ILibraryAppletCreator::CreateLibraryApplet, "CreateLibraryApplet"},
{1, nullptr, "TerminateAllLibraryApplets"},
@@ -961,8 +983,8 @@ void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx)
LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}",
static_cast<u32>(applet_id), applet_mode);
const auto& applet_manager{Core::System::GetInstance().GetAppletManager()};
const auto applet = applet_manager.GetApplet(applet_id, current_process_title_id);
const auto& applet_manager{system.GetAppletManager()};
const auto applet = applet_manager.GetApplet(applet_id);
if (applet == nullptr) {
LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", static_cast<u32>(applet_id));
@@ -999,8 +1021,7 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContex
const auto handle{rp.Pop<Kernel::Handle>()};
const auto transfer_mem =
Core::System::GetInstance().CurrentProcess()->GetHandleTable().Get<Kernel::TransferMemory>(
handle);
system.CurrentProcess()->GetHandleTable().Get<Kernel::TransferMemory>(handle);
if (transfer_mem == nullptr) {
LOG_ERROR(Service_AM, "shared_mem is a nullpr for handle={:08X}", handle);
@@ -1018,7 +1039,8 @@ void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContex
rb.PushIpcInterface(std::make_shared<IStorage>(std::move(memory)));
}
IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") {
IApplicationFunctions::IApplicationFunctions(Core::System& system_)
: ServiceFramework("IApplicationFunctions"), system{system_} {
// clang-format off
static const FunctionInfo functions[] = {
{1, &IApplicationFunctions::PopLaunchParameter, "PopLaunchParameter"},
@@ -1066,7 +1088,7 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
auto& kernel = system.Kernel();
gpu_error_detected_event = Kernel::WritableEvent::CreateEventPair(
kernel, Kernel::ResetType::Manual, "IApplicationFunctions:GpuErrorDetectedSystemEvent");
}
@@ -1111,26 +1133,55 @@ void IApplicationFunctions::EndBlockingHomeButton(Kernel::HLERequestContext& ctx
}
void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
IPC::RequestParser rp{ctx};
const auto kind = rp.PopEnum<LaunchParameterKind>();
LaunchParameters params{};
LOG_DEBUG(Service_AM, "called, kind={:08X}", static_cast<u8>(kind));
params.magic = POP_LAUNCH_PARAMETER_MAGIC;
params.is_account_selected = 1;
if (kind == LaunchParameterKind::ApplicationSpecific && !launch_popped_application_specific) {
const auto backend = BCAT::CreateBackendFromSettings(
[this](u64 tid) { return system.GetFileSystemController().GetBCATDirectory(tid); });
const auto build_id_full = system.GetCurrentProcessBuildID();
u64 build_id{};
std::memcpy(&build_id, build_id_full.data(), sizeof(u64));
Account::ProfileManager profile_manager{};
const auto uuid = profile_manager.GetUser(Settings::values.current_user);
ASSERT(uuid);
params.current_user = uuid->uuid;
const auto data =
backend->GetLaunchParameter({system.CurrentProcess()->GetTitleID(), build_id});
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
if (data.has_value()) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<AM::IStorage>(*data);
launch_popped_application_specific = true;
return;
}
} else if (kind == LaunchParameterKind::AccountPreselectedUser &&
!launch_popped_account_preselect) {
LaunchParameterAccountPreselectedUser params{};
rb.Push(RESULT_SUCCESS);
params.magic = LAUNCH_PARAMETER_ACCOUNT_PRESELECTED_USER_MAGIC;
params.is_account_selected = 1;
std::vector<u8> buffer(sizeof(LaunchParameters));
std::memcpy(buffer.data(), &params, buffer.size());
Account::ProfileManager profile_manager{};
const auto uuid = profile_manager.GetUser(Settings::values.current_user);
ASSERT(uuid);
params.current_user = uuid->uuid;
rb.PushIpcInterface<AM::IStorage>(buffer);
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
std::vector<u8> buffer(sizeof(LaunchParameterAccountPreselectedUser));
std::memcpy(buffer.data(), &params, buffer.size());
rb.PushIpcInterface<AM::IStorage>(buffer);
launch_popped_account_preselect = true;
return;
}
LOG_ERROR(Service_AM, "Attempted to load launch parameter but none was found!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERR_NO_DATA_IN_CHANNEL);
}
void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest(
@@ -1143,13 +1194,21 @@ void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest(
void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
u128 uid = rp.PopRaw<u128>(); // What does this do?
LOG_WARNING(Service, "(STUBBED) called uid = {:016X}{:016X}", uid[1], uid[0]);
u128 user_id = rp.PopRaw<u128>();
LOG_DEBUG(Service_AM, "called, uid={:016X}{:016X}", user_id[1], user_id[0]);
FileSys::SaveDataDescriptor descriptor{};
descriptor.title_id = system.CurrentProcess()->GetTitleID();
descriptor.user_id = user_id;
descriptor.type = FileSys::SaveDataType::SaveData;
const auto res = system.GetFileSystemController().CreateSaveData(
FileSys::SaveDataSpaceId::NandUser, descriptor);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push(res.Code());
rb.Push<u64>(0);
} // namespace Service::AM
}
void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) {
// Takes an input u32 Result, no output.
@@ -1180,7 +1239,7 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) {
// Get supported languages from NACP, if possible
// Default to 0 (all languages supported)
u32 supported_languages = 0;
FileSys::PatchManager pm{Core::System::GetInstance().CurrentProcess()->GetTitleID()};
FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()};
const auto res = pm.GetControlMetadata();
if (res.first != nullptr) {
@@ -1188,8 +1247,8 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) {
}
// Call IApplicationManagerInterface implementation.
auto& service_manager = Core::System::GetInstance().ServiceManager();
auto ns_am2 = service_manager.GetService<Service::NS::NS>("ns:am2");
auto& service_manager = system.ServiceManager();
auto ns_am2 = service_manager.GetService<NS::NS>("ns:am2");
auto app_man = ns_am2->GetApplicationManagerInterface();
// Get desired application language
@@ -1261,8 +1320,8 @@ void IApplicationFunctions::ExtendSaveData(Kernel::HLERequestContext& ctx) {
"new_journal={:016X}",
static_cast<u8>(type), user_id[1], user_id[0], new_normal_size, new_journal_size);
FileSystem::WriteSaveDataSize(type, Core::CurrentProcess()->GetTitleID(), user_id,
{new_normal_size, new_journal_size});
system.GetFileSystemController().WriteSaveDataSize(
type, system.CurrentProcess()->GetTitleID(), user_id, {new_normal_size, new_journal_size});
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
@@ -1281,8 +1340,8 @@ void IApplicationFunctions::GetSaveDataSize(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", static_cast<u8>(type),
user_id[1], user_id[0]);
const auto size =
FileSystem::ReadSaveDataSize(type, Core::CurrentProcess()->GetTitleID(), user_id);
const auto size = system.GetFileSystemController().ReadSaveDataSize(
type, system.CurrentProcess()->GetTitleID(), user_id);
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(RESULT_SUCCESS);
@@ -1300,9 +1359,9 @@ void IApplicationFunctions::GetGpuErrorDetectedSystemEvent(Kernel::HLERequestCon
void InstallInterfaces(SM::ServiceManager& service_manager,
std::shared_ptr<NVFlinger::NVFlinger> nvflinger, Core::System& system) {
auto message_queue = std::make_shared<AppletMessageQueue>();
message_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); // Needed on
// game boot
auto message_queue = std::make_shared<AppletMessageQueue>(system.Kernel());
// Needed on game boot
message_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
std::make_shared<AppletAE>(nvflinger, message_queue, system)->InstallAsService(service_manager);
std::make_shared<AppletOE>(nvflinger, message_queue, system)->InstallAsService(service_manager);
+23 -11
View File
@@ -10,12 +10,15 @@
#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/service.h"
namespace Service {
namespace NVFlinger {
namespace Kernel {
class KernelCore;
}
namespace Service::NVFlinger {
class NVFlinger;
}
namespace AM {
namespace Service::AM {
enum SystemLanguage {
Japanese = 0,
@@ -42,12 +45,13 @@ class AppletMessageQueue {
public:
enum class AppletMessage : u32 {
NoMessage = 0,
ExitRequested = 4,
FocusStateChanged = 15,
OperationModeChanged = 30,
PerformanceModeChanged = 31,
};
AppletMessageQueue();
explicit AppletMessageQueue(Kernel::KernelCore& kernel);
~AppletMessageQueue();
const Kernel::SharedPtr<Kernel::ReadableEvent>& GetMesssageRecieveEvent() const;
@@ -56,6 +60,7 @@ public:
AppletMessage PopMessage();
std::size_t GetMessageCount() const;
void OperationModeChanged();
void RequestExit();
private:
std::queue<AppletMessage> messages;
@@ -65,12 +70,14 @@ private:
class IWindowController final : public ServiceFramework<IWindowController> {
public:
IWindowController();
explicit IWindowController(Core::System& system_);
~IWindowController() override;
private:
void GetAppletResourceUserId(Kernel::HLERequestContext& ctx);
void AcquireForegroundRights(Kernel::HLERequestContext& ctx);
Core::System& system;
};
class IAudioController final : public ServiceFramework<IAudioController> {
@@ -113,10 +120,12 @@ public:
class ISelfController final : public ServiceFramework<ISelfController> {
public:
explicit ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger);
explicit ISelfController(Core::System& system_,
std::shared_ptr<NVFlinger::NVFlinger> nvflinger_);
~ISelfController() override;
private:
void Exit(Kernel::HLERequestContext& ctx);
void LockExit(Kernel::HLERequestContext& ctx);
void UnlockExit(Kernel::HLERequestContext& ctx);
void EnterFatalSection(Kernel::HLERequestContext& ctx);
@@ -138,6 +147,7 @@ private:
void GetAccumulatedSuspendedTickValue(Kernel::HLERequestContext& ctx);
void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx);
Core::System& system;
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
Kernel::EventPair launchable_event;
Kernel::EventPair accumulated_suspended_tick_changed_event;
@@ -208,7 +218,7 @@ private:
class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> {
public:
ILibraryAppletCreator(u64 current_process_title_id);
explicit ILibraryAppletCreator(Core::System& system_);
~ILibraryAppletCreator() override;
private:
@@ -216,12 +226,12 @@ private:
void CreateStorage(Kernel::HLERequestContext& ctx);
void CreateTransferMemoryStorage(Kernel::HLERequestContext& ctx);
u64 current_process_title_id;
Core::System& system;
};
class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> {
public:
IApplicationFunctions();
explicit IApplicationFunctions(Core::System& system_);
~IApplicationFunctions() override;
private:
@@ -244,7 +254,10 @@ private:
void EnableApplicationCrashReport(Kernel::HLERequestContext& ctx);
void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx);
bool launch_popped_application_specific = false;
bool launch_popped_account_preselect = false;
Kernel::EventPair gpu_error_detected_event;
Core::System& system;
};
class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> {
@@ -278,5 +291,4 @@ public:
void InstallInterfaces(SM::ServiceManager& service_manager,
std::shared_ptr<NVFlinger::NVFlinger> nvflinger, Core::System& system);
} // namespace AM
} // namespace Service
} // namespace Service::AM
Loaded 100 of 348 files, more files were not shown because too many files have changed in this diff. Show more