Compare commits

..

149 Commits

Author SHA1 Message Date
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
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
FearlessTobi
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 <zhupengfei321@sina.cn>
2019-09-22 16:42:00 +02: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
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
135 changed files with 5026 additions and 401 deletions

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

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

View File

@@ -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)

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)

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

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

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"

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'

View File

@@ -1,10 +1,9 @@
parameters:
artifactSource: 'true'
cache: 'false'
version: ''
steps:
- script: export DATE=`date '+%Y.%m.%d'` && export CI=true && AZURE_REPO_NAME=yuzu-emu/yuzu-$(BuildName) && AZURE_REPO_TAG=$(BuildName)-$DATE
displayName: 'Determine Build Name'
- task: DockerInstaller@0
displayName: 'Prepare Environment'
inputs:
@@ -15,7 +14,7 @@ steps:
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
- 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'

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)

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)

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

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)'

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)'

View File

@@ -2,7 +2,7 @@ steps:
- task: DownloadPipelineArtifact@2
displayName: 'Download Windows Release'
inputs:
artifactName: 'yuzu-$(BuildName)-windows-mingw'
artifactName: 'yuzu-$(BuildName)-windows-msvc'
buildType: 'current'
targetPath: '$(Build.ArtifactStagingDirectory)'
- task: DownloadPipelineArtifact@2

View File

@@ -1,6 +1,9 @@
trigger:
- master
variables:
DisplayVersion: $[counter(variables['DisplayPrefix'], 1)]
stages:
- stage: format
displayName: 'format'
@@ -15,12 +18,49 @@ stages:
dependsOn: format
displayName: 'build'
jobs:
- template: ./templates/build-standard.yml
parameters:
cache: 'true'
- 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
dependsOn:
- build
- build_win
jobs:
- job: github
displayName: 'GitHub Release'

View File

@@ -0,0 +1,8 @@
trigger:
- master
stages:
- stage: merge
displayName: 'merge'
jobs:
- template: ./templates/merge-private.yml

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)

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
.gitmodules vendored
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

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)

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"

View File

@@ -1,7 +1,6 @@
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/).

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 "")

2
externals/fmt vendored

1
externals/libzip vendored Submodule

Submodule externals/libzip added at bd7a8103e9

1
externals/zlib vendored Submodule

Submodule externals/zlib added at cacf7f1d4e

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.

View File

@@ -15,11 +15,23 @@ if (DEFINED ENV{CI})
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,
@@ -60,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"

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);
}

View File

@@ -713,7 +713,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 +720,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;
}
}

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

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

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
@@ -82,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
@@ -241,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
@@ -499,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)

View File

@@ -111,7 +111,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
}
struct System::Impl {
explicit Impl(System& system)
: kernel{system}, cpu_core_manager{system}, applet_manager{system}, reporter{system} {}
: kernel{system}, fs_controller{system}, cpu_core_manager{system},
applet_manager{system}, reporter{system} {}
Cpu& CurrentCpuCore() {
return cpu_core_manager.GetCurrentCore();
@@ -339,6 +340,7 @@ struct System::Impl {
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;
@@ -640,6 +642,14 @@ 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);
}

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"
@@ -98,6 +97,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;
@@ -330,6 +331,10 @@ public:
bool GetExitLock() const;
void SetCurrentProcessBuildID(const CurrentBuildProcessID& id);
const CurrentBuildProcessID& GetCurrentProcessBuildID() const;
private:
System();
@@ -353,8 +358,4 @@ private:
static System s_instance;
};
inline Kernel::Process* CurrentProcess() {
return System::GetInstance().CurrentProcess();
}
} // namespace Core

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.");

View File

@@ -136,4 +136,9 @@ 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

View File

@@ -61,6 +61,8 @@ public:
u64 GetUserNANDTotalSpace() const;
u64 GetFullNANDTotalSpace() const;
VirtualDir GetBCATDirectory(u64 title_id) const;
private:
VirtualDir nand_root;
VirtualDir load_root;

View File

@@ -35,11 +35,11 @@ void RomFSFactory::SetPackedUpdate(VirtualFile update_raw) {
this->update_raw = std::move(update_raw);
}
ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() const {
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));
}

View File

@@ -33,7 +33,7 @@ public:
~RomFSFactory();
void SetPackedUpdate(VirtualFile update_raw);
ResultVal<VirtualFile> OpenCurrentProcess() const;
ResultVal<VirtualFile> OpenCurrentProcess(u64 current_process_title_id) const;
ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type) const;
private:

View File

@@ -127,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);

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

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

View File

@@ -641,7 +641,8 @@ 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) {

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)) {

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,15 +47,20 @@ 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(Core::System& system_)
: ServiceFramework("IWindowController"), system{system_} {
@@ -1128,26 +1134,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(
@@ -1165,7 +1200,7 @@ void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called, uid={:016X}{:016X}", user_id[1], user_id[0]);
FileSys::SaveDataDescriptor descriptor{};
descriptor.title_id = Core::CurrentProcess()->GetTitleID();
descriptor.title_id = system.CurrentProcess()->GetTitleID();
descriptor.user_id = user_id;
descriptor.type = FileSys::SaveDataType::SaveData;
const auto res = system.GetFileSystemController().CreateSaveData(

View File

@@ -147,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;
@@ -154,8 +155,6 @@ private:
u32 idle_time_detection_extension = 0;
u64 num_fatal_sections_entered = 0;
bool is_auto_sleep_disabled = false;
Core::System& system;
};
class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> {
@@ -255,6 +254,8 @@ 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;
};

View File

@@ -157,6 +157,10 @@ AppletManager::AppletManager(Core::System& system_) : system{system_} {}
AppletManager::~AppletManager() = default;
const AppletFrontendSet& AppletManager::GetAppletFrontendSet() const {
return frontend;
}
void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) {
if (set.parental_controls != nullptr)
frontend.parental_controls = std::move(set.parental_controls);

View File

@@ -190,6 +190,8 @@ public:
explicit AppletManager(Core::System& system_);
~AppletManager();
const AppletFrontendSet& GetAppletFrontendSet() const;
void SetAppletFrontendSet(AppletFrontendSet set);
void SetDefaultAppletFrontendSet();
void SetDefaultAppletsIfMissing();

View File

@@ -13,7 +13,7 @@ constexpr PerformanceConfiguration DEFAULT_PERFORMANCE_CONFIGURATION =
PerformanceConfiguration::Config7;
Controller::Controller(Core::Timing::CoreTiming& core_timing)
: core_timing(core_timing), configs{
: core_timing{core_timing}, configs{
{PerformanceMode::Handheld, DEFAULT_PERFORMANCE_CONFIGURATION},
{PerformanceMode::Docked, DEFAULT_PERFORMANCE_CONFIGURATION},
} {}
@@ -63,6 +63,7 @@ PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(Performa
void Controller::SetClockSpeed(u32 mhz) {
LOG_INFO(Service_APM, "called, mhz={:08X}", mhz);
// TODO(DarkLordZach): Actually signal core_timing to change clock speed.
// TODO(Rodrigo): Remove [[maybe_unused]] when core_timing is used.
}
} // namespace Service::APM

View File

@@ -50,7 +50,7 @@ enum class PerformanceMode : u8 {
// system during times of high load -- this simply maps to different PerformanceConfigs to use.
class Controller {
public:
Controller(Core::Timing::CoreTiming& core_timing);
explicit Controller(Core::Timing::CoreTiming& core_timing);
~Controller();
void SetPerformanceConfiguration(PerformanceMode mode, PerformanceConfiguration config);
@@ -62,9 +62,9 @@ public:
private:
void SetClockSpeed(u32 mhz);
std::map<PerformanceMode, PerformanceConfiguration> configs;
[[maybe_unused]] Core::Timing::CoreTiming& core_timing;
Core::Timing::CoreTiming& core_timing;
std::map<PerformanceMode, PerformanceConfiguration> configs;
};
} // namespace Service::APM

View File

@@ -205,7 +205,7 @@ private:
AudioCore::StreamPtr stream;
std::string device_name;
AudoutParams audio_params{};
[[maybe_unused]] AudoutParams audio_params {};
/// This is the event handle used to check if the audio buffer was released
Kernel::EventPair buffer_event;

View File

@@ -0,0 +1,137 @@
// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/lock.h"
#include "core/hle/service/bcat/backend/backend.h"
namespace Service::BCAT {
ProgressServiceBackend::ProgressServiceBackend(std::string_view event_name) {
auto& kernel{Core::System::GetInstance().Kernel()};
event = Kernel::WritableEvent::CreateEventPair(
kernel, Kernel::ResetType::Automatic,
std::string("ProgressServiceBackend:UpdateEvent:").append(event_name));
}
Kernel::SharedPtr<Kernel::ReadableEvent> ProgressServiceBackend::GetEvent() const {
return event.readable;
}
DeliveryCacheProgressImpl& ProgressServiceBackend::GetImpl() {
return impl;
}
void ProgressServiceBackend::SetNeedHLELock(bool need) {
need_hle_lock = need;
}
void ProgressServiceBackend::SetTotalSize(u64 size) {
impl.total_bytes = size;
SignalUpdate();
}
void ProgressServiceBackend::StartConnecting() {
impl.status = DeliveryCacheProgressImpl::Status::Connecting;
SignalUpdate();
}
void ProgressServiceBackend::StartProcessingDataList() {
impl.status = DeliveryCacheProgressImpl::Status::ProcessingDataList;
SignalUpdate();
}
void ProgressServiceBackend::StartDownloadingFile(std::string_view dir_name,
std::string_view file_name, u64 file_size) {
impl.status = DeliveryCacheProgressImpl::Status::Downloading;
impl.current_downloaded_bytes = 0;
impl.current_total_bytes = file_size;
std::memcpy(impl.current_directory.data(), dir_name.data(),
std::min<u64>(dir_name.size(), 0x31ull));
std::memcpy(impl.current_file.data(), file_name.data(),
std::min<u64>(file_name.size(), 0x31ull));
SignalUpdate();
}
void ProgressServiceBackend::UpdateFileProgress(u64 downloaded) {
impl.current_downloaded_bytes = downloaded;
SignalUpdate();
}
void ProgressServiceBackend::FinishDownloadingFile() {
impl.total_downloaded_bytes += impl.current_total_bytes;
SignalUpdate();
}
void ProgressServiceBackend::CommitDirectory(std::string_view dir_name) {
impl.status = DeliveryCacheProgressImpl::Status::Committing;
impl.current_file.fill(0);
impl.current_downloaded_bytes = 0;
impl.current_total_bytes = 0;
std::memcpy(impl.current_directory.data(), dir_name.data(),
std::min<u64>(dir_name.size(), 0x31ull));
SignalUpdate();
}
void ProgressServiceBackend::FinishDownload(ResultCode result) {
impl.total_downloaded_bytes = impl.total_bytes;
impl.status = DeliveryCacheProgressImpl::Status::Done;
impl.result = result;
SignalUpdate();
}
void ProgressServiceBackend::SignalUpdate() const {
if (need_hle_lock) {
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
event.writable->Signal();
} else {
event.writable->Signal();
}
}
Backend::Backend(DirectoryGetter getter) : dir_getter(std::move(getter)) {}
Backend::~Backend() = default;
NullBackend::NullBackend(DirectoryGetter getter) : Backend(std::move(getter)) {}
NullBackend::~NullBackend() = default;
bool NullBackend::Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) {
LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}", title.title_id,
title.build_id);
progress.FinishDownload(RESULT_SUCCESS);
return true;
}
bool NullBackend::SynchronizeDirectory(TitleIDVersion title, std::string name,
ProgressServiceBackend& progress) {
LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}, name={}", title.title_id,
title.build_id, name);
progress.FinishDownload(RESULT_SUCCESS);
return true;
}
bool NullBackend::Clear(u64 title_id) {
LOG_DEBUG(Service_BCAT, "called, title_id={:016X}");
return true;
}
void NullBackend::SetPassphrase(u64 title_id, const Passphrase& passphrase) {
LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase = {}", title_id,
Common::HexToString(passphrase));
}
std::optional<std::vector<u8>> NullBackend::GetLaunchParameter(TitleIDVersion title) {
LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}", title.title_id,
title.build_id);
return std::nullopt;
}
} // namespace Service::BCAT

View File

@@ -0,0 +1,150 @@
// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <functional>
#include <optional>
#include <string>
#include <string_view>
#include "common/common_types.h"
#include "core/file_sys/vfs_types.h"
#include "core/hle/kernel/readable_event.h"
#include "core/hle/kernel/writable_event.h"
#include "core/hle/result.h"
namespace Service::BCAT {
struct DeliveryCacheProgressImpl;
using DirectoryGetter = std::function<FileSys::VirtualDir(u64)>;
using Passphrase = std::array<u8, 0x20>;
struct TitleIDVersion {
u64 title_id;
u64 build_id;
};
using DirectoryName = std::array<char, 0x20>;
using FileName = std::array<char, 0x20>;
struct DeliveryCacheProgressImpl {
enum class Status : s32 {
None = 0x0,
Queued = 0x1,
Connecting = 0x2,
ProcessingDataList = 0x3,
Downloading = 0x4,
Committing = 0x5,
Done = 0x9,
};
Status status;
ResultCode result = RESULT_SUCCESS;
DirectoryName current_directory;
FileName current_file;
s64 current_downloaded_bytes; ///< Bytes downloaded on current file.
s64 current_total_bytes; ///< Bytes total on current file.
s64 total_downloaded_bytes; ///< Bytes downloaded on overall download.
s64 total_bytes; ///< Bytes total on overall download.
INSERT_PADDING_BYTES(
0x198); ///< Appears to be unused in official code, possibly reserved for future use.
};
static_assert(sizeof(DeliveryCacheProgressImpl) == 0x200,
"DeliveryCacheProgressImpl has incorrect size.");
// A class to manage the signalling to the game about BCAT download progress.
// Some of this class is implemented in module.cpp to avoid exposing the implementation structure.
class ProgressServiceBackend {
friend class IBcatService;
public:
// Clients should call this with true if any of the functions are going to be called from a
// non-HLE thread and this class need to lock the hle mutex. (default is false)
void SetNeedHLELock(bool need);
// Sets the number of bytes total in the entire download.
void SetTotalSize(u64 size);
// Notifies the application that the backend has started connecting to the server.
void StartConnecting();
// Notifies the application that the backend has begun accumulating and processing metadata.
void StartProcessingDataList();
// Notifies the application that a file is starting to be downloaded.
void StartDownloadingFile(std::string_view dir_name, std::string_view file_name, u64 file_size);
// Updates the progress of the current file to the size passed.
void UpdateFileProgress(u64 downloaded);
// Notifies the application that the current file has completed download.
void FinishDownloadingFile();
// Notifies the application that all files in this directory have completed and are being
// finalized.
void CommitDirectory(std::string_view dir_name);
// Notifies the application that the operation completed with result code result.
void FinishDownload(ResultCode result);
private:
explicit ProgressServiceBackend(std::string_view event_name);
Kernel::SharedPtr<Kernel::ReadableEvent> GetEvent() const;
DeliveryCacheProgressImpl& GetImpl();
void SignalUpdate() const;
DeliveryCacheProgressImpl impl{};
Kernel::EventPair event;
bool need_hle_lock = false;
};
// A class representing an abstract backend for BCAT functionality.
class Backend {
public:
explicit Backend(DirectoryGetter getter);
virtual ~Backend();
// Called when the backend is needed to synchronize the data for the game with title ID and
// version in title. A ProgressServiceBackend object is provided to alert the application of
// status.
virtual bool Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) = 0;
// Very similar to Synchronize, but only for the directory provided. Backends should not alter
// the data for any other directories.
virtual bool SynchronizeDirectory(TitleIDVersion title, std::string name,
ProgressServiceBackend& progress) = 0;
// Removes all cached data associated with title id provided.
virtual bool Clear(u64 title_id) = 0;
// Sets the BCAT Passphrase to be used with the associated title ID.
virtual void SetPassphrase(u64 title_id, const Passphrase& passphrase) = 0;
// Gets the launch parameter used by AM associated with the title ID and version provided.
virtual std::optional<std::vector<u8>> GetLaunchParameter(TitleIDVersion title) = 0;
protected:
DirectoryGetter dir_getter;
};
// A backend of BCAT that provides no operation.
class NullBackend : public Backend {
public:
explicit NullBackend(DirectoryGetter getter);
~NullBackend() override;
bool Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) override;
bool SynchronizeDirectory(TitleIDVersion title, std::string name,
ProgressServiceBackend& progress) override;
bool Clear(u64 title_id) override;
void SetPassphrase(u64 title_id, const Passphrase& passphrase) override;
std::optional<std::vector<u8>> GetLaunchParameter(TitleIDVersion title) override;
};
std::unique_ptr<Backend> CreateBackendFromSettings(DirectoryGetter getter);
} // namespace Service::BCAT

View File

@@ -0,0 +1,504 @@
// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <fmt/ostream.h>
#include <httplib.h>
#include <json.hpp>
#include <mbedtls/sha256.h>
#include "common/hex_util.h"
#include "common/logging/backend.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/file_sys/vfs.h"
#include "core/file_sys/vfs_libzip.h"
#include "core/file_sys/vfs_vector.h"
#include "core/frontend/applets/error.h"
#include "core/hle/service/am/applets/applets.h"
#include "core/hle/service/bcat/backend/boxcat.h"
#include "core/settings.h"
namespace {
// Prevents conflicts with windows macro called CreateFile
FileSys::VirtualFile VfsCreateFileWrap(FileSys::VirtualDir dir, std::string_view name) {
return dir->CreateFile(name);
}
// Prevents conflicts with windows macro called DeleteFile
bool VfsDeleteFileWrap(FileSys::VirtualDir dir, std::string_view name) {
return dir->DeleteFile(name);
}
} // Anonymous namespace
namespace Service::BCAT {
constexpr ResultCode ERROR_GENERAL_BCAT_FAILURE{ErrorModule::BCAT, 1};
constexpr char BOXCAT_HOSTNAME[] = "api.yuzu-emu.org";
// Formatted using fmt with arg[0] = hex title id
constexpr char BOXCAT_PATHNAME_DATA[] = "/game-assets/{:016X}/boxcat";
constexpr char BOXCAT_PATHNAME_LAUNCHPARAM[] = "/game-assets/{:016X}/launchparam";
constexpr char BOXCAT_PATHNAME_EVENTS[] = "/game-assets/boxcat/events";
constexpr char BOXCAT_API_VERSION[] = "1";
constexpr char BOXCAT_CLIENT_TYPE[] = "yuzu";
// HTTP status codes for Boxcat
enum class ResponseStatus {
Ok = 200, ///< Operation completed successfully.
BadClientVersion = 301, ///< The Boxcat-Client-Version doesn't match the server.
NoUpdate = 304, ///< The digest provided would match the new data, no need to update.
NoMatchTitleId = 404, ///< The title ID provided doesn't have a boxcat implementation.
NoMatchBuildId = 406, ///< The build ID provided is blacklisted (potentially because of format
///< issues or whatnot) and has no data.
};
enum class DownloadResult {
Success = 0,
NoResponse,
GeneralWebError,
NoMatchTitleId,
NoMatchBuildId,
InvalidContentType,
GeneralFSError,
BadClientVersion,
};
constexpr std::array<const char*, 8> DOWNLOAD_RESULT_LOG_MESSAGES{
"Success",
"There was no response from the server.",
"There was a general web error code returned from the server.",
"The title ID of the current game doesn't have a boxcat implementation. If you believe an "
"implementation should be added, contact yuzu support.",
"The build ID of the current version of the game is marked as incompatible with the current "
"BCAT distribution. Try upgrading or downgrading your game version or contacting yuzu support.",
"The content type of the web response was invalid.",
"There was a general filesystem error while saving the zip file.",
"The server is either too new or too old to serve the request. Try using the latest version of "
"an official release of yuzu.",
};
std::ostream& operator<<(std::ostream& os, DownloadResult result) {
return os << DOWNLOAD_RESULT_LOG_MESSAGES.at(static_cast<std::size_t>(result));
}
constexpr u32 PORT = 443;
constexpr u32 TIMEOUT_SECONDS = 30;
[[maybe_unused]] constexpr u64 VFS_COPY_BLOCK_SIZE = 1ULL << 24; // 4MB
namespace {
std::string GetBINFilePath(u64 title_id) {
return fmt::format("{}bcat/{:016X}/launchparam.bin",
FileUtil::GetUserPath(FileUtil::UserPath::CacheDir), title_id);
}
std::string GetZIPFilePath(u64 title_id) {
return fmt::format("{}bcat/{:016X}/data.zip",
FileUtil::GetUserPath(FileUtil::UserPath::CacheDir), title_id);
}
// If the error is something the user should know about (build ID mismatch, bad client version),
// display an error.
void HandleDownloadDisplayResult(DownloadResult res) {
if (res == DownloadResult::Success || res == DownloadResult::NoResponse ||
res == DownloadResult::GeneralWebError || res == DownloadResult::GeneralFSError ||
res == DownloadResult::NoMatchTitleId || res == DownloadResult::InvalidContentType) {
return;
}
const auto& frontend{Core::System::GetInstance().GetAppletManager().GetAppletFrontendSet()};
frontend.error->ShowCustomErrorText(
ResultCode(-1), "There was an error while attempting to use Boxcat.",
DOWNLOAD_RESULT_LOG_MESSAGES[static_cast<std::size_t>(res)], [] {});
}
bool VfsRawCopyProgress(FileSys::VirtualFile src, FileSys::VirtualFile dest,
std::string_view dir_name, ProgressServiceBackend& progress,
std::size_t block_size = 0x1000) {
if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
return false;
if (!dest->Resize(src->GetSize()))
return false;
progress.StartDownloadingFile(dir_name, src->GetName(), src->GetSize());
std::vector<u8> temp(std::min(block_size, src->GetSize()));
for (std::size_t i = 0; i < src->GetSize(); i += block_size) {
const auto read = std::min(block_size, src->GetSize() - i);
if (src->Read(temp.data(), read, i) != read) {
return false;
}
if (dest->Write(temp.data(), read, i) != read) {
return false;
}
progress.UpdateFileProgress(i);
}
progress.FinishDownloadingFile();
return true;
}
bool VfsRawCopyDProgressSingle(FileSys::VirtualDir src, FileSys::VirtualDir dest,
ProgressServiceBackend& progress, std::size_t block_size = 0x1000) {
if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
return false;
for (const auto& file : src->GetFiles()) {
const auto out_file = VfsCreateFileWrap(dest, file->GetName());
if (!VfsRawCopyProgress(file, out_file, src->GetName(), progress, block_size)) {
return false;
}
}
progress.CommitDirectory(src->GetName());
return true;
}
bool VfsRawCopyDProgress(FileSys::VirtualDir src, FileSys::VirtualDir dest,
ProgressServiceBackend& progress, std::size_t block_size = 0x1000) {
if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
return false;
for (const auto& dir : src->GetSubdirectories()) {
const auto out = dest->CreateSubdirectory(dir->GetName());
if (!VfsRawCopyDProgressSingle(dir, out, progress, block_size)) {
return false;
}
}
return true;
}
} // Anonymous namespace
class Boxcat::Client {
public:
Client(std::string path, u64 title_id, u64 build_id)
: path(std::move(path)), title_id(title_id), build_id(build_id) {}
DownloadResult DownloadDataZip() {
return DownloadInternal(fmt::format(BOXCAT_PATHNAME_DATA, title_id), TIMEOUT_SECONDS,
"application/zip");
}
DownloadResult DownloadLaunchParam() {
return DownloadInternal(fmt::format(BOXCAT_PATHNAME_LAUNCHPARAM, title_id),
TIMEOUT_SECONDS / 3, "application/octet-stream");
}
private:
DownloadResult DownloadInternal(const std::string& resolved_path, u32 timeout_seconds,
const std::string& content_type_name) {
if (client == nullptr) {
client = std::make_unique<httplib::SSLClient>(BOXCAT_HOSTNAME, PORT, timeout_seconds);
}
httplib::Headers headers{
{std::string("Game-Assets-API-Version"), std::string(BOXCAT_API_VERSION)},
{std::string("Boxcat-Client-Type"), std::string(BOXCAT_CLIENT_TYPE)},
{std::string("Game-Build-Id"), fmt::format("{:016X}", build_id)},
};
if (FileUtil::Exists(path)) {
FileUtil::IOFile file{path, "rb"};
if (file.IsOpen()) {
std::vector<u8> bytes(file.GetSize());
file.ReadBytes(bytes.data(), bytes.size());
const auto digest = DigestFile(bytes);
headers.insert({std::string("If-None-Match"), Common::HexToString(digest, false)});
}
}
const auto response = client->Get(resolved_path.c_str(), headers);
if (response == nullptr)
return DownloadResult::NoResponse;
if (response->status == static_cast<int>(ResponseStatus::NoUpdate))
return DownloadResult::Success;
if (response->status == static_cast<int>(ResponseStatus::BadClientVersion))
return DownloadResult::BadClientVersion;
if (response->status == static_cast<int>(ResponseStatus::NoMatchTitleId))
return DownloadResult::NoMatchTitleId;
if (response->status == static_cast<int>(ResponseStatus::NoMatchBuildId))
return DownloadResult::NoMatchBuildId;
if (response->status != static_cast<int>(ResponseStatus::Ok))
return DownloadResult::GeneralWebError;
const auto content_type = response->headers.find("content-type");
if (content_type == response->headers.end() ||
content_type->second.find(content_type_name) == std::string::npos) {
return DownloadResult::InvalidContentType;
}
FileUtil::CreateFullPath(path);
FileUtil::IOFile file{path, "wb"};
if (!file.IsOpen())
return DownloadResult::GeneralFSError;
if (!file.Resize(response->body.size()))
return DownloadResult::GeneralFSError;
if (file.WriteBytes(response->body.data(), response->body.size()) != response->body.size())
return DownloadResult::GeneralFSError;
return DownloadResult::Success;
}
using Digest = std::array<u8, 0x20>;
static Digest DigestFile(std::vector<u8> bytes) {
Digest out{};
mbedtls_sha256(bytes.data(), bytes.size(), out.data(), 0);
return out;
}
std::unique_ptr<httplib::Client> client;
std::string path;
u64 title_id;
u64 build_id;
};
Boxcat::Boxcat(DirectoryGetter getter) : Backend(std::move(getter)) {}
Boxcat::~Boxcat() = default;
void SynchronizeInternal(DirectoryGetter dir_getter, TitleIDVersion title,
ProgressServiceBackend& progress,
std::optional<std::string> dir_name = {}) {
progress.SetNeedHLELock(true);
if (Settings::values.bcat_boxcat_local) {
LOG_INFO(Service_BCAT, "Boxcat using local data by override, skipping download.");
const auto dir = dir_getter(title.title_id);
if (dir)
progress.SetTotalSize(dir->GetSize());
progress.FinishDownload(RESULT_SUCCESS);
return;
}
const auto zip_path{GetZIPFilePath(title.title_id)};
Boxcat::Client client{zip_path, title.title_id, title.build_id};
progress.StartConnecting();
const auto res = client.DownloadDataZip();
if (res != DownloadResult::Success) {
LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res);
if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) {
FileUtil::Delete(zip_path);
}
HandleDownloadDisplayResult(res);
progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE);
return;
}
progress.StartProcessingDataList();
FileUtil::IOFile zip{zip_path, "rb"};
const auto size = zip.GetSize();
std::vector<u8> bytes(size);
if (!zip.IsOpen() || size == 0 || zip.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) {
LOG_ERROR(Service_BCAT, "Boxcat failed to read ZIP file at path '{}'!", zip_path);
progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE);
return;
}
const auto extracted = FileSys::ExtractZIP(std::make_shared<FileSys::VectorVfsFile>(bytes));
if (extracted == nullptr) {
LOG_ERROR(Service_BCAT, "Boxcat failed to extract ZIP file!");
progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE);
return;
}
if (dir_name == std::nullopt) {
progress.SetTotalSize(extracted->GetSize());
const auto target_dir = dir_getter(title.title_id);
if (target_dir == nullptr || !VfsRawCopyDProgress(extracted, target_dir, progress)) {
LOG_ERROR(Service_BCAT, "Boxcat failed to copy extracted ZIP to target directory!");
progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE);
return;
}
} else {
const auto target_dir = dir_getter(title.title_id);
if (target_dir == nullptr) {
LOG_ERROR(Service_BCAT, "Boxcat failed to get directory for title ID!");
progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE);
return;
}
const auto target_sub = target_dir->GetSubdirectory(*dir_name);
const auto source_sub = extracted->GetSubdirectory(*dir_name);
progress.SetTotalSize(source_sub->GetSize());
std::vector<std::string> filenames;
{
const auto files = target_sub->GetFiles();
std::transform(files.begin(), files.end(), std::back_inserter(filenames),
[](const auto& vfile) { return vfile->GetName(); });
}
for (const auto& filename : filenames) {
VfsDeleteFileWrap(target_sub, filename);
}
if (target_sub == nullptr || source_sub == nullptr ||
!VfsRawCopyDProgressSingle(source_sub, target_sub, progress)) {
LOG_ERROR(Service_BCAT, "Boxcat failed to copy extracted ZIP to target directory!");
progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE);
return;
}
}
progress.FinishDownload(RESULT_SUCCESS);
}
bool Boxcat::Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) {
is_syncing.exchange(true);
std::thread([this, title, &progress] { SynchronizeInternal(dir_getter, title, progress); })
.detach();
return true;
}
bool Boxcat::SynchronizeDirectory(TitleIDVersion title, std::string name,
ProgressServiceBackend& progress) {
is_syncing.exchange(true);
std::thread(
[this, title, name, &progress] { SynchronizeInternal(dir_getter, title, progress, name); })
.detach();
return true;
}
bool Boxcat::Clear(u64 title_id) {
if (Settings::values.bcat_boxcat_local) {
LOG_INFO(Service_BCAT, "Boxcat using local data by override, skipping clear.");
return true;
}
const auto dir = dir_getter(title_id);
std::vector<std::string> dirnames;
for (const auto& subdir : dir->GetSubdirectories())
dirnames.push_back(subdir->GetName());
for (const auto& subdir : dirnames) {
if (!dir->DeleteSubdirectoryRecursive(subdir))
return false;
}
return true;
}
void Boxcat::SetPassphrase(u64 title_id, const Passphrase& passphrase) {
LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase={}", title_id,
Common::HexToString(passphrase));
}
std::optional<std::vector<u8>> Boxcat::GetLaunchParameter(TitleIDVersion title) {
const auto path{GetBINFilePath(title.title_id)};
if (Settings::values.bcat_boxcat_local) {
LOG_INFO(Service_BCAT, "Boxcat using local data by override, skipping download.");
} else {
Boxcat::Client client{path, title.title_id, title.build_id};
const auto res = client.DownloadLaunchParam();
if (res != DownloadResult::Success) {
LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res);
if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) {
FileUtil::Delete(path);
}
HandleDownloadDisplayResult(res);
return std::nullopt;
}
}
FileUtil::IOFile bin{path, "rb"};
const auto size = bin.GetSize();
std::vector<u8> bytes(size);
if (!bin.IsOpen() || size == 0 || bin.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) {
LOG_ERROR(Service_BCAT, "Boxcat failed to read launch parameter binary at path '{}'!",
path);
return std::nullopt;
}
return bytes;
}
Boxcat::StatusResult Boxcat::GetStatus(std::optional<std::string>& global,
std::map<std::string, EventStatus>& games) {
httplib::SSLClient client{BOXCAT_HOSTNAME, static_cast<int>(PORT),
static_cast<int>(TIMEOUT_SECONDS)};
httplib::Headers headers{
{std::string("Game-Assets-API-Version"), std::string(BOXCAT_API_VERSION)},
{std::string("Boxcat-Client-Type"), std::string(BOXCAT_CLIENT_TYPE)},
};
const auto response = client.Get(BOXCAT_PATHNAME_EVENTS, headers);
if (response == nullptr)
return StatusResult::Offline;
if (response->status == static_cast<int>(ResponseStatus::BadClientVersion))
return StatusResult::BadClientVersion;
try {
nlohmann::json json = nlohmann::json::parse(response->body);
if (!json["online"].get<bool>())
return StatusResult::Offline;
if (json["global"].is_null())
global = std::nullopt;
else
global = json["global"].get<std::string>();
if (json["games"].is_array()) {
for (const auto object : json["games"]) {
if (object.is_object() && object.find("name") != object.end()) {
EventStatus detail{};
if (object["header"].is_string()) {
detail.header = object["header"].get<std::string>();
} else {
detail.header = std::nullopt;
}
if (object["footer"].is_string()) {
detail.footer = object["footer"].get<std::string>();
} else {
detail.footer = std::nullopt;
}
if (object["events"].is_array()) {
for (const auto& event : object["events"]) {
if (!event.is_string())
continue;
detail.events.push_back(event.get<std::string>());
}
}
games.insert_or_assign(object["name"], std::move(detail));
}
}
}
return StatusResult::Success;
} catch (const nlohmann::json::parse_error& error) {
LOG_ERROR(Service_BCAT, "{}", error.what());
return StatusResult::ParseError;
}
}
} // namespace Service::BCAT

View File

@@ -0,0 +1,58 @@
// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <atomic>
#include <map>
#include <optional>
#include "core/hle/service/bcat/backend/backend.h"
namespace Service::BCAT {
struct EventStatus {
std::optional<std::string> header;
std::optional<std::string> footer;
std::vector<std::string> events;
};
/// Boxcat is yuzu's custom backend implementation of Nintendo's BCAT service. It is free to use and
/// doesn't require a switch or nintendo account. The content is controlled by the yuzu team.
class Boxcat final : public Backend {
friend void SynchronizeInternal(DirectoryGetter dir_getter, TitleIDVersion title,
ProgressServiceBackend& progress,
std::optional<std::string> dir_name);
public:
explicit Boxcat(DirectoryGetter getter);
~Boxcat() override;
bool Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) override;
bool SynchronizeDirectory(TitleIDVersion title, std::string name,
ProgressServiceBackend& progress) override;
bool Clear(u64 title_id) override;
void SetPassphrase(u64 title_id, const Passphrase& passphrase) override;
std::optional<std::vector<u8>> GetLaunchParameter(TitleIDVersion title) override;
enum class StatusResult {
Success,
Offline,
ParseError,
BadClientVersion,
};
static StatusResult GetStatus(std::optional<std::string>& global,
std::map<std::string, EventStatus>& games);
private:
std::atomic_bool is_syncing{false};
class Client;
std::unique_ptr<Client> client;
};
} // namespace Service::BCAT

View File

@@ -6,11 +6,16 @@
namespace Service::BCAT {
BCAT::BCAT(std::shared_ptr<Module> module, const char* name)
: Module::Interface(std::move(module), name) {
BCAT::BCAT(Core::System& system, std::shared_ptr<Module> module,
FileSystem::FileSystemController& fsc, const char* name)
: Interface(system, std::move(module), fsc, name) {
// clang-format off
static const FunctionInfo functions[] = {
{0, &BCAT::CreateBcatService, "CreateBcatService"},
{1, &BCAT::CreateDeliveryCacheStorageService, "CreateDeliveryCacheStorageService"},
{2, &BCAT::CreateDeliveryCacheStorageServiceWithApplicationId, "CreateDeliveryCacheStorageServiceWithApplicationId"},
};
// clang-format on
RegisterHandlers(functions);
}

View File

@@ -6,11 +6,16 @@
#include "core/hle/service/bcat/module.h"
namespace Core {
class System;
}
namespace Service::BCAT {
class BCAT final : public Module::Interface {
public:
explicit BCAT(std::shared_ptr<Module> module, const char* name);
explicit BCAT(Core::System& system, std::shared_ptr<Module> module,
FileSystem::FileSystemController& fsc, const char* name);
~BCAT() override;
};

View File

@@ -2,34 +2,257 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cctype>
#include <mbedtls/md5.h>
#include "backend/boxcat.h"
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/file_sys/vfs.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/readable_event.h"
#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/bcat/backend/backend.h"
#include "core/hle/service/bcat/bcat.h"
#include "core/hle/service/bcat/module.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/settings.h"
namespace Service::BCAT {
constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::BCAT, 1};
constexpr ResultCode ERROR_FAILED_OPEN_ENTITY{ErrorModule::BCAT, 2};
constexpr ResultCode ERROR_ENTITY_ALREADY_OPEN{ErrorModule::BCAT, 6};
constexpr ResultCode ERROR_NO_OPEN_ENTITY{ErrorModule::BCAT, 7};
// The command to clear the delivery cache just calls fs IFileSystem DeleteFile on all of the files
// and if any of them have a non-zero result it just forwards that result. This is the FS error code
// for permission denied, which is the closest approximation of this scenario.
constexpr ResultCode ERROR_FAILED_CLEAR_CACHE{ErrorModule::FS, 6400};
using BCATDigest = std::array<u8, 0x10>;
namespace {
u64 GetCurrentBuildID(const Core::System::CurrentBuildProcessID& id) {
u64 out{};
std::memcpy(&out, id.data(), sizeof(u64));
return out;
}
// The digest is only used to determine if a file is unique compared to others of the same name.
// Since the algorithm isn't ever checked in game, MD5 is safe.
BCATDigest DigestFile(const FileSys::VirtualFile& file) {
BCATDigest out{};
const auto bytes = file->ReadAllBytes();
mbedtls_md5(bytes.data(), bytes.size(), out.data());
return out;
}
// For a name to be valid it must be non-empty, must have a null terminating character as the final
// char, can only contain numbers, letters, underscores and a hyphen if directory and a period if
// file.
bool VerifyNameValidInternal(Kernel::HLERequestContext& ctx, std::array<char, 0x20> name,
char match_char) {
const auto null_chars = std::count(name.begin(), name.end(), 0);
const auto bad_chars = std::count_if(name.begin(), name.end(), [match_char](char c) {
return !std::isalnum(static_cast<u8>(c)) && c != '_' && c != match_char && c != '\0';
});
if (null_chars == 0x20 || null_chars == 0 || bad_chars != 0 || name[0x1F] != '\0') {
LOG_ERROR(Service_BCAT, "Name passed was invalid!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_ARGUMENT);
return false;
}
return true;
}
bool VerifyNameValidDir(Kernel::HLERequestContext& ctx, DirectoryName name) {
return VerifyNameValidInternal(ctx, name, '-');
}
bool VerifyNameValidFile(Kernel::HLERequestContext& ctx, FileName name) {
return VerifyNameValidInternal(ctx, name, '.');
}
} // Anonymous namespace
struct DeliveryCacheDirectoryEntry {
FileName name;
u64 size;
BCATDigest digest;
};
class IDeliveryCacheProgressService final : public ServiceFramework<IDeliveryCacheProgressService> {
public:
IDeliveryCacheProgressService(Kernel::SharedPtr<Kernel::ReadableEvent> event,
const DeliveryCacheProgressImpl& impl)
: ServiceFramework{"IDeliveryCacheProgressService"}, event(std::move(event)), impl(impl) {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IDeliveryCacheProgressService::GetEvent, "GetEvent"},
{1, &IDeliveryCacheProgressService::GetImpl, "GetImpl"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
void GetEvent(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_BCAT, "called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(event);
}
void GetImpl(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_BCAT, "called");
ctx.WriteBuffer(&impl, sizeof(DeliveryCacheProgressImpl));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
Kernel::SharedPtr<Kernel::ReadableEvent> event;
const DeliveryCacheProgressImpl& impl;
};
class IBcatService final : public ServiceFramework<IBcatService> {
public:
IBcatService() : ServiceFramework("IBcatService") {
explicit IBcatService(Core::System& system_, Backend& backend_)
: ServiceFramework("IBcatService"), system{system_}, backend{backend_} {
// clang-format off
static const FunctionInfo functions[] = {
{10100, nullptr, "RequestSyncDeliveryCache"},
{10101, nullptr, "RequestSyncDeliveryCacheWithDirectoryName"},
{10100, &IBcatService::RequestSyncDeliveryCache, "RequestSyncDeliveryCache"},
{10101, &IBcatService::RequestSyncDeliveryCacheWithDirectoryName, "RequestSyncDeliveryCacheWithDirectoryName"},
{10200, nullptr, "CancelSyncDeliveryCacheRequest"},
{20100, nullptr, "RequestSyncDeliveryCacheWithApplicationId"},
{20101, nullptr, "RequestSyncDeliveryCacheWithApplicationIdAndDirectoryName"},
{30100, nullptr, "SetPassphrase"},
{30100, &IBcatService::SetPassphrase, "SetPassphrase"},
{30200, nullptr, "RegisterBackgroundDeliveryTask"},
{30201, nullptr, "UnregisterBackgroundDeliveryTask"},
{30202, nullptr, "BlockDeliveryTask"},
{30203, nullptr, "UnblockDeliveryTask"},
{90100, nullptr, "EnumerateBackgroundDeliveryTask"},
{90200, nullptr, "GetDeliveryList"},
{90201, nullptr, "ClearDeliveryCacheStorage"},
{90201, &IBcatService::ClearDeliveryCacheStorage, "ClearDeliveryCacheStorage"},
{90300, nullptr, "GetPushNotificationLog"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
enum class SyncType {
Normal,
Directory,
Count,
};
std::shared_ptr<IDeliveryCacheProgressService> CreateProgressService(SyncType type) {
auto& backend{progress.at(static_cast<std::size_t>(type))};
return std::make_shared<IDeliveryCacheProgressService>(backend.GetEvent(),
backend.GetImpl());
}
void RequestSyncDeliveryCache(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_BCAT, "called");
backend.Synchronize({system.CurrentProcess()->GetTitleID(),
GetCurrentBuildID(system.GetCurrentProcessBuildID())},
progress.at(static_cast<std::size_t>(SyncType::Normal)));
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface(CreateProgressService(SyncType::Normal));
}
void RequestSyncDeliveryCacheWithDirectoryName(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto name_raw = rp.PopRaw<DirectoryName>();
const auto name =
Common::StringFromFixedZeroTerminatedBuffer(name_raw.data(), name_raw.size());
LOG_DEBUG(Service_BCAT, "called, name={}", name);
backend.SynchronizeDirectory({system.CurrentProcess()->GetTitleID(),
GetCurrentBuildID(system.GetCurrentProcessBuildID())},
name,
progress.at(static_cast<std::size_t>(SyncType::Directory)));
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface(CreateProgressService(SyncType::Directory));
}
void SetPassphrase(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto title_id = rp.PopRaw<u64>();
const auto passphrase_raw = ctx.ReadBuffer();
LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase={}", title_id,
Common::HexToString(passphrase_raw));
if (title_id == 0) {
LOG_ERROR(Service_BCAT, "Invalid title ID!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_ARGUMENT);
}
if (passphrase_raw.size() > 0x40) {
LOG_ERROR(Service_BCAT, "Passphrase too large!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_ARGUMENT);
return;
}
Passphrase passphrase{};
std::memcpy(passphrase.data(), passphrase_raw.data(),
std::min(passphrase.size(), passphrase_raw.size()));
backend.SetPassphrase(title_id, passphrase);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void ClearDeliveryCacheStorage(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto title_id = rp.PopRaw<u64>();
LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", title_id);
if (title_id == 0) {
LOG_ERROR(Service_BCAT, "Invalid title ID!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_INVALID_ARGUMENT);
return;
}
if (!backend.Clear(title_id)) {
LOG_ERROR(Service_BCAT, "Could not clear the directory successfully!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_FAILED_CLEAR_CACHE);
return;
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
Core::System& system;
Backend& backend;
std::array<ProgressServiceBackend, static_cast<std::size_t>(SyncType::Count)> progress{
ProgressServiceBackend{"Normal"},
ProgressServiceBackend{"Directory"},
};
};
void Module::Interface::CreateBcatService(Kernel::HLERequestContext& ctx) {
@@ -37,20 +260,332 @@ void Module::Interface::CreateBcatService(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IBcatService>();
rb.PushIpcInterface<IBcatService>(system, *backend);
}
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
: ServiceFramework(name), module(std::move(module)) {}
class IDeliveryCacheFileService final : public ServiceFramework<IDeliveryCacheFileService> {
public:
IDeliveryCacheFileService(FileSys::VirtualDir root_)
: ServiceFramework{"IDeliveryCacheFileService"}, root(std::move(root_)) {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IDeliveryCacheFileService::Open, "Open"},
{1, &IDeliveryCacheFileService::Read, "Read"},
{2, &IDeliveryCacheFileService::GetSize, "GetSize"},
{3, &IDeliveryCacheFileService::GetDigest, "GetDigest"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
void Open(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto dir_name_raw = rp.PopRaw<DirectoryName>();
const auto file_name_raw = rp.PopRaw<FileName>();
const auto dir_name =
Common::StringFromFixedZeroTerminatedBuffer(dir_name_raw.data(), dir_name_raw.size());
const auto file_name =
Common::StringFromFixedZeroTerminatedBuffer(file_name_raw.data(), file_name_raw.size());
LOG_DEBUG(Service_BCAT, "called, dir_name={}, file_name={}", dir_name, file_name);
if (!VerifyNameValidDir(ctx, dir_name_raw) || !VerifyNameValidFile(ctx, file_name_raw))
return;
if (current_file != nullptr) {
LOG_ERROR(Service_BCAT, "A file has already been opened on this interface!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_ENTITY_ALREADY_OPEN);
return;
}
const auto dir = root->GetSubdirectory(dir_name);
if (dir == nullptr) {
LOG_ERROR(Service_BCAT, "The directory of name={} couldn't be opened!", dir_name);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_FAILED_OPEN_ENTITY);
return;
}
current_file = dir->GetFile(file_name);
if (current_file == nullptr) {
LOG_ERROR(Service_BCAT, "The file of name={} couldn't be opened!", file_name);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_FAILED_OPEN_ENTITY);
return;
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void Read(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto offset{rp.PopRaw<u64>()};
auto size = ctx.GetWriteBufferSize();
LOG_DEBUG(Service_BCAT, "called, offset={:016X}, size={:016X}", offset, size);
if (current_file == nullptr) {
LOG_ERROR(Service_BCAT, "There is no file currently open!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_NO_OPEN_ENTITY);
}
size = std::min<u64>(current_file->GetSize() - offset, size);
const auto buffer = current_file->ReadBytes(size, offset);
ctx.WriteBuffer(buffer);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(buffer.size());
}
void GetSize(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_BCAT, "called");
if (current_file == nullptr) {
LOG_ERROR(Service_BCAT, "There is no file currently open!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_NO_OPEN_ENTITY);
}
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(current_file->GetSize());
}
void GetDigest(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_BCAT, "called");
if (current_file == nullptr) {
LOG_ERROR(Service_BCAT, "There is no file currently open!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_NO_OPEN_ENTITY);
}
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(RESULT_SUCCESS);
rb.PushRaw(DigestFile(current_file));
}
FileSys::VirtualDir root;
FileSys::VirtualFile current_file;
};
class IDeliveryCacheDirectoryService final
: public ServiceFramework<IDeliveryCacheDirectoryService> {
public:
IDeliveryCacheDirectoryService(FileSys::VirtualDir root_)
: ServiceFramework{"IDeliveryCacheDirectoryService"}, root(std::move(root_)) {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IDeliveryCacheDirectoryService::Open, "Open"},
{1, &IDeliveryCacheDirectoryService::Read, "Read"},
{2, &IDeliveryCacheDirectoryService::GetCount, "GetCount"},
};
// clang-format on
RegisterHandlers(functions);
}
private:
void Open(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto name_raw = rp.PopRaw<DirectoryName>();
const auto name =
Common::StringFromFixedZeroTerminatedBuffer(name_raw.data(), name_raw.size());
LOG_DEBUG(Service_BCAT, "called, name={}", name);
if (!VerifyNameValidDir(ctx, name_raw))
return;
if (current_dir != nullptr) {
LOG_ERROR(Service_BCAT, "A file has already been opened on this interface!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_ENTITY_ALREADY_OPEN);
return;
}
current_dir = root->GetSubdirectory(name);
if (current_dir == nullptr) {
LOG_ERROR(Service_BCAT, "Failed to open the directory name={}!", name);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_FAILED_OPEN_ENTITY);
return;
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void Read(Kernel::HLERequestContext& ctx) {
auto write_size = ctx.GetWriteBufferSize() / sizeof(DeliveryCacheDirectoryEntry);
LOG_DEBUG(Service_BCAT, "called, write_size={:016X}", write_size);
if (current_dir == nullptr) {
LOG_ERROR(Service_BCAT, "There is no open directory!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_NO_OPEN_ENTITY);
return;
}
const auto files = current_dir->GetFiles();
write_size = std::min<u64>(write_size, files.size());
std::vector<DeliveryCacheDirectoryEntry> entries(write_size);
std::transform(
files.begin(), files.begin() + write_size, entries.begin(), [](const auto& file) {
FileName name{};
std::memcpy(name.data(), file->GetName().data(),
std::min(file->GetName().size(), name.size()));
return DeliveryCacheDirectoryEntry{name, file->GetSize(), DigestFile(file)};
});
ctx.WriteBuffer(entries);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u32>(write_size * sizeof(DeliveryCacheDirectoryEntry)));
}
void GetCount(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_BCAT, "called");
if (current_dir == nullptr) {
LOG_ERROR(Service_BCAT, "There is no open directory!");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_NO_OPEN_ENTITY);
return;
}
const auto files = current_dir->GetFiles();
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u32>(files.size()));
}
FileSys::VirtualDir root;
FileSys::VirtualDir current_dir;
};
class IDeliveryCacheStorageService final : public ServiceFramework<IDeliveryCacheStorageService> {
public:
IDeliveryCacheStorageService(FileSys::VirtualDir root_)
: ServiceFramework{"IDeliveryCacheStorageService"}, root(std::move(root_)) {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IDeliveryCacheStorageService::CreateFileService, "CreateFileService"},
{1, &IDeliveryCacheStorageService::CreateDirectoryService, "CreateDirectoryService"},
{10, &IDeliveryCacheStorageService::EnumerateDeliveryCacheDirectory, "EnumerateDeliveryCacheDirectory"},
};
// clang-format on
RegisterHandlers(functions);
for (const auto& subdir : root->GetSubdirectories()) {
DirectoryName name{};
std::memcpy(name.data(), subdir->GetName().data(),
std::min(sizeof(DirectoryName) - 1, subdir->GetName().size()));
entries.push_back(name);
}
}
private:
void CreateFileService(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_BCAT, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IDeliveryCacheFileService>(root);
}
void CreateDirectoryService(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_BCAT, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IDeliveryCacheDirectoryService>(root);
}
void EnumerateDeliveryCacheDirectory(Kernel::HLERequestContext& ctx) {
auto size = ctx.GetWriteBufferSize() / sizeof(DirectoryName);
LOG_DEBUG(Service_BCAT, "called, size={:016X}", size);
size = std::min<u64>(size, entries.size() - next_read_index);
ctx.WriteBuffer(entries.data() + next_read_index, size * sizeof(DirectoryName));
next_read_index += size;
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u32>(size));
}
FileSys::VirtualDir root;
std::vector<DirectoryName> entries;
u64 next_read_index = 0;
};
void Module::Interface::CreateDeliveryCacheStorageService(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_BCAT, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IDeliveryCacheStorageService>(
fsc.GetBCATDirectory(system.CurrentProcess()->GetTitleID()));
}
void Module::Interface::CreateDeliveryCacheStorageServiceWithApplicationId(
Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto title_id = rp.PopRaw<u64>();
LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", title_id);
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IDeliveryCacheStorageService>(fsc.GetBCATDirectory(title_id));
}
std::unique_ptr<Backend> CreateBackendFromSettings(DirectoryGetter getter) {
const auto backend = Settings::values.bcat_backend;
#ifdef YUZU_ENABLE_BOXCAT
if (backend == "boxcat")
return std::make_unique<Boxcat>(std::move(getter));
#endif
return std::make_unique<NullBackend>(std::move(getter));
}
Module::Interface::Interface(Core::System& system_, std::shared_ptr<Module> module_,
FileSystem::FileSystemController& fsc_, const char* name)
: ServiceFramework(name), fsc{fsc_}, module{std::move(module_)},
backend{CreateBackendFromSettings([&fsc_](u64 tid) { return fsc_.GetBCATDirectory(tid); })},
system{system_} {}
Module::Interface::~Interface() = default;
void InstallInterfaces(SM::ServiceManager& service_manager) {
void InstallInterfaces(Core::System& system) {
auto module = std::make_shared<Module>();
std::make_shared<BCAT>(module, "bcat:a")->InstallAsService(service_manager);
std::make_shared<BCAT>(module, "bcat:m")->InstallAsService(service_manager);
std::make_shared<BCAT>(module, "bcat:u")->InstallAsService(service_manager);
std::make_shared<BCAT>(module, "bcat:s")->InstallAsService(service_manager);
std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:a")
->InstallAsService(system.ServiceManager());
std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:m")
->InstallAsService(system.ServiceManager());
std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:u")
->InstallAsService(system.ServiceManager());
std::make_shared<BCAT>(system, module, system.GetFileSystemController(), "bcat:s")
->InstallAsService(system.ServiceManager());
}
} // namespace Service::BCAT

View File

@@ -6,23 +6,46 @@
#include "core/hle/service/service.h"
namespace Service::BCAT {
namespace Core {
class System;
}
namespace Service {
namespace FileSystem {
class FileSystemController;
} // namespace FileSystem
namespace BCAT {
class Backend;
class Module final {
public:
class Interface : public ServiceFramework<Interface> {
public:
explicit Interface(std::shared_ptr<Module> module, const char* name);
explicit Interface(Core::System& system_, std::shared_ptr<Module> module_,
FileSystem::FileSystemController& fsc_, const char* name);
~Interface() override;
void CreateBcatService(Kernel::HLERequestContext& ctx);
void CreateDeliveryCacheStorageService(Kernel::HLERequestContext& ctx);
void CreateDeliveryCacheStorageServiceWithApplicationId(Kernel::HLERequestContext& ctx);
protected:
FileSystem::FileSystemController& fsc;
std::shared_ptr<Module> module;
std::unique_ptr<Backend> backend;
private:
Core::System& system;
};
};
/// Registers all BCAT services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager);
void InstallInterfaces(Core::System& system);
} // namespace Service::BCAT
} // namespace BCAT
} // namespace Service

View File

@@ -66,7 +66,7 @@ enum class FatalType : u32 {
static void GenerateErrorReport(Core::System& system, ResultCode error_code,
const FatalInfo& info) {
const auto title_id = Core::CurrentProcess()->GetTitleID();
const auto title_id = system.CurrentProcess()->GetTitleID();
std::string crash_report = fmt::format(
"Yuzu {}-{} crash report\n"
"Title ID: {:016x}\n"

View File

@@ -241,7 +241,7 @@ ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
return FileSys::ERROR_PATH_NOT_FOUND;
}
FileSystemController::FileSystemController() = default;
FileSystemController::FileSystemController(Core::System& system_) : system{system_} {}
FileSystemController::~FileSystemController() = default;
@@ -290,7 +290,7 @@ ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFSCurrentProcess()
return ResultCode(-1);
}
return romfs_factory->OpenCurrentProcess();
return romfs_factory->OpenCurrentProcess(system.CurrentProcess()->GetTitleID());
}
ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFS(
@@ -447,10 +447,10 @@ FileSys::SaveDataSize FileSystemController::ReadSaveDataSize(FileSys::SaveDataTy
FileSys::SaveDataSize new_size{SUFFICIENT_SAVE_DATA_SIZE, SUFFICIENT_SAVE_DATA_SIZE};
FileSys::NACP nacp;
const auto res = Core::System::GetInstance().GetAppLoader().ReadControlData(nacp);
const auto res = system.GetAppLoader().ReadControlData(nacp);
if (res != Loader::ResultStatus::Success) {
FileSys::PatchManager pm{Core::CurrentProcess()->GetTitleID()};
FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()};
auto [nacp_unique, discard] = pm.GetControlMetadata();
if (nacp_unique != nullptr) {
@@ -674,6 +674,15 @@ FileSys::VirtualDir FileSystemController::GetModificationDumpRoot(u64 title_id)
return bis_factory->GetModificationDumpRoot(title_id);
}
FileSys::VirtualDir FileSystemController::GetBCATDirectory(u64 title_id) const {
LOG_TRACE(Service_FS, "Opening BCAT root for tid={:016X}", title_id);
if (bis_factory == nullptr)
return nullptr;
return bis_factory->GetBCATDirectory(title_id);
}
void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
if (overwrite) {
bis_factory = nullptr;
@@ -693,10 +702,10 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove
if (bis_factory == nullptr) {
bis_factory =
std::make_unique<FileSys::BISFactory>(nand_directory, load_directory, dump_directory);
Core::System::GetInstance().RegisterContentProvider(
FileSys::ContentProviderUnionSlot::SysNAND, bis_factory->GetSystemNANDContents());
Core::System::GetInstance().RegisterContentProvider(
FileSys::ContentProviderUnionSlot::UserNAND, bis_factory->GetUserNANDContents());
system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::SysNAND,
bis_factory->GetSystemNANDContents());
system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::UserNAND,
bis_factory->GetUserNANDContents());
}
if (save_data_factory == nullptr) {
@@ -705,8 +714,8 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove
if (sdmc_factory == nullptr) {
sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory));
Core::System::GetInstance().RegisterContentProvider(FileSys::ContentProviderUnionSlot::SDMC,
sdmc_factory->GetSDMCContents());
system.RegisterContentProvider(FileSys::ContentProviderUnionSlot::SDMC,
sdmc_factory->GetSDMCContents());
}
}

View File

@@ -10,6 +10,10 @@
#include "core/file_sys/vfs.h"
#include "core/hle/result.h"
namespace Core {
class System;
}
namespace FileSys {
class BISFactory;
class RegisteredCache;
@@ -52,7 +56,7 @@ enum class ImageDirectoryId : u32 {
class FileSystemController {
public:
FileSystemController();
explicit FileSystemController(Core::System& system_);
~FileSystemController();
ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory);
@@ -110,6 +114,8 @@ public:
FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) const;
FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) const;
FileSys::VirtualDir GetBCATDirectory(u64 title_id) const;
// Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function
// above is called.
void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true);
@@ -123,6 +129,8 @@ private:
std::unique_ptr<FileSys::XCI> gamecard;
std::unique_ptr<FileSys::RegisteredCache> gamecard_registered;
std::unique_ptr<FileSys::PlaceholderCache> gamecard_placeholder;
Core::System& system;
};
void InstallInterfaces(Core::System& system);

View File

@@ -803,7 +803,7 @@ void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto save_struct = rp.PopRaw<FileSys::SaveDataDescriptor>();
auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>();
[[maybe_unused]] auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>();
u128 uid = rp.PopRaw<u128>();
LOG_DEBUG(Service_FS, "called save_struct = {}, uid = {:016X}{:016X}", save_struct.DebugInfo(),

View File

@@ -237,7 +237,6 @@ private:
};
Common::UUID uuid;
bool is_event_created = false;
Kernel::EventPair notification_event;
std::queue<SizedNotificationInfo> notifications;
States states{};

View File

@@ -11,11 +11,10 @@
namespace Service::HID {
constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
[[maybe_unused]] constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
enum class JoystickId : std::size_t { Joystick_Left, Joystick_Right };
Controller_DebugPad::Controller_DebugPad(Core::System& system)
: ControllerBase(system), system(system) {}
Controller_DebugPad::Controller_DebugPad(Core::System& system) : ControllerBase(system) {}
Controller_DebugPad::~Controller_DebugPad() = default;
void Controller_DebugPad::OnInit() {}

View File

@@ -89,6 +89,5 @@ private:
buttons;
std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>
analogs;
Core::System& system;
};
} // namespace Service::HID

View File

@@ -10,8 +10,7 @@
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00;
Controller_Gesture::Controller_Gesture(Core::System& system)
: ControllerBase(system), system(system) {}
Controller_Gesture::Controller_Gesture(Core::System& system) : ControllerBase(system) {}
Controller_Gesture::~Controller_Gesture() = default;
void Controller_Gesture::OnInit() {}

View File

@@ -59,6 +59,5 @@ private:
std::array<GestureState, 17> gesture_states;
};
SharedMemory shared_memory{};
Core::System& system;
};
} // namespace Service::HID

View File

@@ -12,8 +12,7 @@ namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
constexpr u8 KEYS_PER_BYTE = 8;
Controller_Keyboard::Controller_Keyboard(Core::System& system)
: ControllerBase(system), system(system) {}
Controller_Keyboard::Controller_Keyboard(Core::System& system) : ControllerBase(system) {}
Controller_Keyboard::~Controller_Keyboard() = default;
void Controller_Keyboard::OnInit() {}

View File

@@ -53,6 +53,5 @@ private:
keyboard_keys;
std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardMods>
keyboard_mods;
Core::System& system;
};
} // namespace Service::HID

View File

@@ -11,7 +11,7 @@
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400;
Controller_Mouse::Controller_Mouse(Core::System& system) : ControllerBase(system), system(system) {}
Controller_Mouse::Controller_Mouse(Core::System& system) : ControllerBase(system) {}
Controller_Mouse::~Controller_Mouse() = default;
void Controller_Mouse::OnInit() {}

View File

@@ -53,6 +53,5 @@ private:
std::unique_ptr<Input::MouseDevice> mouse_device;
std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeMouseButton::NumMouseButtons>
mouse_button_devices;
Core::System& system;
};
} // namespace Service::HID

View File

@@ -20,7 +20,7 @@
namespace Service::HID {
constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
[[maybe_unused]] constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
constexpr std::size_t NPAD_OFFSET = 0x9A00;
constexpr u32 BATTERY_FULL = 2;
constexpr u32 MAX_NPAD_ID = 7;
@@ -105,6 +105,8 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
controller.joy_styles.raw = 0; // Zero out
controller.device_type.raw = 0;
switch (controller_type) {
case NPadControllerType::None:
UNREACHABLE();
case NPadControllerType::Handheld:
controller.joy_styles.handheld.Assign(1);
controller.device_type.handheld.Assign(1);
@@ -239,7 +241,7 @@ void Controller_NPad::OnRelease() {}
void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
const auto controller_idx = NPadIdToIndex(npad_id);
const auto controller_type = connected_controllers[controller_idx].type;
[[maybe_unused]] const auto controller_type = connected_controllers[controller_idx].type;
if (!connected_controllers[controller_idx].is_connected) {
return;
}
@@ -346,6 +348,8 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
libnx_entry.connection_status.raw = 0;
switch (controller_type) {
case NPadControllerType::None:
UNREACHABLE();
case NPadControllerType::Handheld:
handheld_entry.connection_status.raw = 0;
handheld_entry.connection_status.IsWired.Assign(1);

View File

@@ -9,8 +9,7 @@
namespace Service::HID {
Controller_Stubbed::Controller_Stubbed(Core::System& system)
: ControllerBase(system), system(system) {}
Controller_Stubbed::Controller_Stubbed(Core::System& system) : ControllerBase(system) {}
Controller_Stubbed::~Controller_Stubbed() = default;
void Controller_Stubbed::OnInit() {}

View File

@@ -30,6 +30,5 @@ public:
private:
bool smart_update{};
std::size_t common_offset{};
Core::System& system;
};
} // namespace Service::HID

View File

@@ -13,8 +13,7 @@
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400;
Controller_Touchscreen::Controller_Touchscreen(Core::System& system)
: ControllerBase(system), system(system) {}
Controller_Touchscreen::Controller_Touchscreen(Core::System& system) : ControllerBase(system) {}
Controller_Touchscreen::~Controller_Touchscreen() = default;
void Controller_Touchscreen::OnInit() {}

View File

@@ -69,6 +69,5 @@ private:
TouchScreenSharedMemory shared_memory{};
std::unique_ptr<Input::TouchDevice> touch_device;
s64_le last_touch{};
Core::System& system;
};
} // namespace Service::HID

View File

@@ -10,7 +10,7 @@
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00;
Controller_XPad::Controller_XPad(Core::System& system) : ControllerBase(system), system(system) {}
Controller_XPad::Controller_XPad(Core::System& system) : ControllerBase(system) {}
Controller_XPad::~Controller_XPad() = default;
void Controller_XPad::OnInit() {}

View File

@@ -56,6 +56,5 @@ private:
};
static_assert(sizeof(SharedMemory) == 0x1000, "SharedMemory is an invalid size");
SharedMemory shared_memory{};
Core::System& system;
};
} // namespace Service::HID

View File

@@ -38,8 +38,10 @@ namespace Service::HID {
// Updating period for each HID device.
// TODO(ogniK): Find actual polling rate of hid
constexpr s64 pad_update_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 66);
constexpr s64 accelerometer_update_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 100);
constexpr s64 gyroscope_update_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 100);
[[maybe_unused]] constexpr s64 accelerometer_update_ticks =
static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 100);
[[maybe_unused]] constexpr s64 gyroscope_update_ticks =
static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 100);
constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
IAppletResource::IAppletResource(Core::System& system)
@@ -193,7 +195,7 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
{101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"},
{102, &Hid::SetSupportedNpadIdType, "SetSupportedNpadIdType"},
{103, &Hid::ActivateNpad, "ActivateNpad"},
{104, nullptr, "DeactivateNpad"},
{104, &Hid::DeactivateNpad, "DeactivateNpad"},
{106, &Hid::AcquireNpadStyleSetUpdateEventHandle, "AcquireNpadStyleSetUpdateEventHandle"},
{107, &Hid::DisconnectNpad, "DisconnectNpad"},
{108, &Hid::GetPlayerLedPattern, "GetPlayerLedPattern"},
@@ -468,6 +470,17 @@ void Hid::ActivateNpad(Kernel::HLERequestContext& ctx) {
applet_resource->ActivateController(HidController::NPad);
}
void Hid::DeactivateNpad(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
applet_resource->DeactivateController(HidController::NPad);
}
void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto npad_id{rp.Pop<u32>()};

View File

@@ -99,6 +99,7 @@ private:
void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx);
void ActivateNpad(Kernel::HLERequestContext& ctx);
void DeactivateNpad(Kernel::HLERequestContext& ctx);
void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx);
void DisconnectNpad(Kernel::HLERequestContext& ctx);
void GetPlayerLedPattern(Kernel::HLERequestContext& ctx);

View File

@@ -163,7 +163,7 @@ public:
return;
}
if (Core::CurrentProcess()->GetTitleID() != header.title_id) {
if (system.CurrentProcess()->GetTitleID() != header.title_id) {
LOG_ERROR(Service_LDR,
"Attempting to load NRR with title ID other than current process. (actual "
"{:016X})!",
@@ -327,7 +327,7 @@ public:
}
// Load NRO as new executable module
auto* process = Core::CurrentProcess();
auto* process = system.CurrentProcess();
auto& vm_manager = process->VMManager();
auto map_address = vm_manager.FindFreeRegion(nro_size + bss_size);
@@ -411,7 +411,7 @@ public:
return;
}
auto& vm_manager = Core::CurrentProcess()->VMManager();
auto& vm_manager = system.CurrentProcess()->VMManager();
const auto& nro_info = iter->second;
// Unmap the mirrored memory

View File

@@ -18,8 +18,8 @@
namespace Service::NFP {
namespace ErrCodes {
constexpr ResultCode ERR_TAG_FAILED(ErrorModule::NFP,
-1); // TODO(ogniK): Find the actual error code
[[maybe_unused]] constexpr ResultCode ERR_TAG_FAILED(ErrorModule::NFP,
-1); // TODO(ogniK): Find the actual error code
constexpr ResultCode ERR_NO_APPLICATION_AREA(ErrorModule::NFP, 152);
} // namespace ErrCodes
@@ -35,7 +35,7 @@ Module::Interface::~Interface() = default;
class IUser final : public ServiceFramework<IUser> {
public:
IUser(Module::Interface& nfp_interface, Core::System& system)
: ServiceFramework("NFP::IUser"), nfp_interface(nfp_interface), system(system) {
: ServiceFramework("NFP::IUser"), nfp_interface(nfp_interface) {
static const FunctionInfo functions[] = {
{0, &IUser::Initialize, "Initialize"},
{1, &IUser::Finalize, "Finalize"},
@@ -183,6 +183,8 @@ private:
case DeviceState::TagRemoved:
device_state = DeviceState::Initialized;
break;
default:
break;
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -324,7 +326,6 @@ private:
Kernel::EventPair deactivate_event;
Kernel::EventPair availability_change_event;
const Module::Interface& nfp_interface;
Core::System& system;
};
void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {

View File

@@ -12,6 +12,13 @@
namespace Service::NIFM {
enum class RequestState : u32 {
NotSubmitted = 1,
Error = 1, ///< The duplicate 1 is intentional; it means both not submitted and error on HW.
Pending = 2,
Connected = 3,
};
class IScanRequest final : public ServiceFramework<IScanRequest> {
public:
explicit IScanRequest() : ServiceFramework("IScanRequest") {
@@ -81,7 +88,7 @@ private:
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0);
rb.PushEnum(RequestState::Connected);
}
void GetResult(Kernel::HLERequestContext& ctx) {
@@ -189,14 +196,14 @@ private:
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u8>(0);
rb.Push<u8>(1);
}
void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u8>(0);
rb.Push<u8>(1);
}
Core::System& system;
};

View File

@@ -324,14 +324,14 @@ void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) {
void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) {
// Map backing memory for the font data
LOG_DEBUG(Service_NS, "called");
Core::CurrentProcess()->VMManager().MapMemoryBlock(SHARED_FONT_MEM_VADDR, impl->shared_font, 0,
SHARED_FONT_MEM_SIZE,
Kernel::MemoryState::Shared);
system.CurrentProcess()->VMManager().MapMemoryBlock(SHARED_FONT_MEM_VADDR, impl->shared_font, 0,
SHARED_FONT_MEM_SIZE,
Kernel::MemoryState::Shared);
// Create shared font memory object
auto& kernel = system.Kernel();
impl->shared_font_mem = Kernel::SharedMemory::Create(
kernel, Core::CurrentProcess(), SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite,
kernel, system.CurrentProcess(), SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite,
Kernel::MemoryPermission::Read, SHARED_FONT_MEM_VADDR, Kernel::MemoryRegion::BASE,
"PL_U:shared_font_mem");

View File

@@ -45,6 +45,8 @@ u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std:
return GetVARegions(input, output);
case IoctlCommand::IocUnmapBufferCommand:
return UnmapBuffer(input, output);
default:
break;
}
if (static_cast<IoctlCommand>(command.cmd.Value()) == IoctlCommand::IocRemapCommand)

View File

@@ -38,9 +38,10 @@ u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, const std::v
return IocCtrlEventUnregister(input, output);
case IoctlCommand::IocCtrlEventSignalCommand:
return IocCtrlEventSignal(input, output);
default:
UNIMPLEMENTED_MSG("Unimplemented ioctl");
return 0;
}
UNIMPLEMENTED_MSG("Unimplemented ioctl");
return 0;
}
u32 nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) {

View File

@@ -40,9 +40,10 @@ u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input,
return FlushL2(input, output);
case IoctlCommand::IocGetGpuTime:
return GetGpuTime(input, output);
default:
UNIMPLEMENTED_MSG("Unimplemented ioctl");
return 0;
}
UNIMPLEMENTED_MSG("Unimplemented ioctl");
return 0;
}
u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output,

View File

@@ -44,6 +44,8 @@ u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std::ve
return GetWaitbase(input, output);
case IoctlCommand::IocChannelSetTimeoutCommand:
return ChannelSetTimeout(input, output);
default:
break;
}
if (command.group == NVGPU_IOCTL_MAGIC) {

View File

@@ -208,7 +208,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) {
AOC::InstallInterfaces(*sm, system);
APM::InstallInterfaces(system);
Audio::InstallInterfaces(*sm, system);
BCAT::InstallInterfaces(*sm);
BCAT::InstallInterfaces(system);
BPC::InstallInterfaces(*sm);
BtDrv::InstallInterfaces(*sm, system);
BTM::InstallInterfaces(*sm, system);

View File

@@ -150,6 +150,7 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
// Apply cheats if they exist and the program has a valid title ID
if (pm) {
auto& system = Core::System::GetInstance();
system.SetCurrentProcessBuildID(nso_header.build_id);
const auto cheats = pm->CreateCheatList(system, nso_header.build_id);
if (!cheats.empty()) {
system.RegisterCheatList(cheats, nso_header.build_id, load_base, image_size);

View File

@@ -146,7 +146,7 @@ static u8* GetPointerFromVMA(const Kernel::Process& process, VAddr vaddr) {
* using a VMA from the current process.
*/
static u8* GetPointerFromVMA(VAddr vaddr) {
return GetPointerFromVMA(*Core::CurrentProcess(), vaddr);
return GetPointerFromVMA(*Core::System::GetInstance().CurrentProcess(), vaddr);
}
template <typename T>
@@ -226,7 +226,7 @@ bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) {
}
bool IsValidVirtualAddress(const VAddr vaddr) {
return IsValidVirtualAddress(*Core::CurrentProcess(), vaddr);
return IsValidVirtualAddress(*Core::System::GetInstance().CurrentProcess(), vaddr);
}
bool IsKernelVirtualAddress(const VAddr vaddr) {
@@ -387,7 +387,7 @@ void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_
}
void ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_t size) {
ReadBlock(*Core::CurrentProcess(), src_addr, dest_buffer, size);
ReadBlock(*Core::System::GetInstance().CurrentProcess(), src_addr, dest_buffer, size);
}
void Write8(const VAddr addr, const u8 data) {
@@ -450,7 +450,7 @@ void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const voi
}
void WriteBlock(const VAddr dest_addr, const void* src_buffer, const std::size_t size) {
WriteBlock(*Core::CurrentProcess(), dest_addr, src_buffer, size);
WriteBlock(*Core::System::GetInstance().CurrentProcess(), dest_addr, src_buffer, size);
}
void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const std::size_t size) {
@@ -539,7 +539,7 @@ void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr,
}
void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size) {
CopyBlock(*Core::CurrentProcess(), dest_addr, src_addr, size);
CopyBlock(*Core::System::GetInstance().CurrentProcess(), dest_addr, src_addr, size);
}
} // namespace Memory

View File

@@ -103,6 +103,8 @@ void LogSettings() {
LogSetting("Debugging_UseGdbstub", Settings::values.use_gdbstub);
LogSetting("Debugging_GdbstubPort", Settings::values.gdbstub_port);
LogSetting("Debugging_ProgramArgs", Settings::values.program_args);
LogSetting("Services_BCATBackend", Settings::values.bcat_backend);
LogSetting("Services_BCATBoxcatLocal", Settings::values.bcat_boxcat_local);
}
} // namespace Settings

View File

@@ -448,6 +448,10 @@ struct Values {
bool reporting_services;
bool quest_flag;
// BCAT
std::string bcat_backend;
bool bcat_boxcat_local;
// WebService
bool enable_telemetry;
std::string web_api_url;

View File

@@ -105,9 +105,15 @@ add_library(video_core STATIC
shader/decode/warp.cpp
shader/decode/xmad.cpp
shader/decode/other.cpp
shader/ast.cpp
shader/ast.h
shader/control_flow.cpp
shader/control_flow.h
shader/compiler_settings.cpp
shader/compiler_settings.h
shader/decode.cpp
shader/expr.cpp
shader/expr.h
shader/node_helper.cpp
shader/node_helper.h
shader/node.h

View File

@@ -19,6 +19,7 @@
#include "video_core/renderer_opengl/gl_device.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
#include "video_core/shader/ast.h"
#include "video_core/shader/node.h"
#include "video_core/shader/shader_ir.h"
@@ -242,6 +243,26 @@ constexpr const char* GetTypeString(Type type) {
}
}
constexpr const char* GetImageTypeDeclaration(Tegra::Shader::ImageType image_type) {
switch (image_type) {
case Tegra::Shader::ImageType::Texture1D:
return "1D";
case Tegra::Shader::ImageType::TextureBuffer:
return "Buffer";
case Tegra::Shader::ImageType::Texture1DArray:
return "1DArray";
case Tegra::Shader::ImageType::Texture2D:
return "2D";
case Tegra::Shader::ImageType::Texture2DArray:
return "2DArray";
case Tegra::Shader::ImageType::Texture3D:
return "3D";
default:
UNREACHABLE();
return "1D";
}
}
/// Generates code to use for a swizzle operation.
constexpr const char* GetSwizzle(u32 element) {
constexpr std::array swizzle = {".x", ".y", ".z", ".w"};
@@ -314,39 +335,24 @@ constexpr bool IsVertexShader(ProgramType stage) {
return stage == ProgramType::VertexA || stage == ProgramType::VertexB;
}
class ASTDecompiler;
class ExprDecompiler;
class GLSLDecompiler final {
public:
explicit GLSLDecompiler(const Device& device, const ShaderIR& ir, ProgramType stage,
std::string suffix)
: device{device}, ir{ir}, stage{stage}, suffix{suffix}, header{ir.GetHeader()} {}
void Decompile() {
DeclareVertex();
DeclareGeometry();
DeclareRegisters();
DeclarePredicates();
DeclareLocalMemory();
DeclareSharedMemory();
DeclareInternalFlags();
DeclareInputAttributes();
DeclareOutputAttributes();
DeclareConstantBuffers();
DeclareGlobalMemory();
DeclareSamplers();
DeclarePhysicalAttributeReader();
DeclareImages();
code.AddLine("void execute_{}() {{", suffix);
++code.scope;
void DecompileBranchMode() {
// VM's program counter
const auto first_address = ir.GetBasicBlocks().begin()->first;
code.AddLine("uint jmp_to = {}U;", first_address);
// TODO(Subv): Figure out the actual depth of the flow stack, for now it seems
// unlikely that shaders will use 20 nested SSYs and PBKs.
constexpr u32 FLOW_STACK_SIZE = 20;
if (!ir.IsFlowStackDisabled()) {
constexpr u32 FLOW_STACK_SIZE = 20;
for (const auto stack : std::array{MetaStackClass::Ssy, MetaStackClass::Pbk}) {
code.AddLine("uint {}[{}];", FlowStackName(stack), FLOW_STACK_SIZE);
code.AddLine("uint {} = 0U;", FlowStackTopName(stack));
@@ -372,10 +378,37 @@ public:
code.AddLine("default: return;");
code.AddLine("}}");
for (std::size_t i = 0; i < 2; ++i) {
--code.scope;
code.AddLine("}}");
--code.scope;
code.AddLine("}}");
}
void DecompileAST();
void Decompile() {
DeclareVertex();
DeclareGeometry();
DeclareRegisters();
DeclarePredicates();
DeclareLocalMemory();
DeclareInternalFlags();
DeclareInputAttributes();
DeclareOutputAttributes();
DeclareConstantBuffers();
DeclareGlobalMemory();
DeclareSamplers();
DeclarePhysicalAttributeReader();
code.AddLine("void execute_{}() {{", suffix);
++code.scope;
if (ir.IsDecompiled()) {
DecompileAST();
} else {
DecompileBranchMode();
}
--code.scope;
code.AddLine("}}");
}
std::string GetResult() {
@@ -404,6 +437,9 @@ public:
}
private:
friend class ASTDecompiler;
friend class ExprDecompiler;
void DeclareVertex() {
if (!IsVertexShader(stage))
return;
@@ -721,26 +757,6 @@ private:
void DeclareImages() {
const auto& images{ir.GetImages()};
for (const auto& [offset, image] : images) {
const char* image_type = [&] {
switch (image.GetType()) {
case Tegra::Shader::ImageType::Texture1D:
return "1D";
case Tegra::Shader::ImageType::TextureBuffer:
return "Buffer";
case Tegra::Shader::ImageType::Texture1DArray:
return "1DArray";
case Tegra::Shader::ImageType::Texture2D:
return "2D";
case Tegra::Shader::ImageType::Texture2DArray:
return "2DArray";
case Tegra::Shader::ImageType::Texture3D:
return "3D";
default:
UNREACHABLE();
return "1D";
}
}();
std::string qualifier = "coherent volatile";
if (image.IsRead() && !image.IsWritten()) {
qualifier += " readonly";
@@ -748,13 +764,10 @@ private:
qualifier += " writeonly";
}
std::string format;
if (image.IsAtomic()) {
format = "r32ui, ";
}
const char* format = image.IsAtomic() ? "r32ui, " : "";
const char* type_declaration = GetImageTypeDeclaration(image.GetType());
code.AddLine("layout ({}binding = IMAGE_BINDING_{}) {} uniform uimage{} {};", format,
image.GetIndex(), qualifier, image_type, GetImage(image));
image.GetIndex(), qualifier, type_declaration, GetImage(image));
}
if (!images.empty()) {
code.AddNewLine();
@@ -1494,6 +1507,8 @@ private:
case Tegra::Shader::HalfType::H1_H1:
return {fmt::format("vec2({}[1])", operand.AsHalfFloat()), Type::HalfFloat};
}
UNREACHABLE();
return {"0", Type::Int};
}
Expression HMergeF32(Operation operation) {
@@ -1822,10 +1837,9 @@ private:
return {};
}
Expression Exit(Operation operation) {
void PreExit() {
if (stage != ProgramType::Fragment) {
code.AddLine("return;");
return {};
return;
}
const auto& used_registers = ir.GetRegisters();
const auto SafeGetRegister = [&](u32 reg) -> Expression {
@@ -1857,7 +1871,10 @@ private:
// already contains one past the last color register.
code.AddLine("gl_FragDepth = {};", SafeGetRegister(current_reg + 1).AsFloat());
}
}
Expression Exit(Operation operation) {
PreExit();
code.AddLine("return;");
return {};
}
@@ -2254,6 +2271,208 @@ private:
ShaderWriter code;
};
static constexpr std::string_view flow_var = "flow_var_";
std::string GetFlowVariable(u32 i) {
return fmt::format("{}{}", flow_var, i);
}
class ExprDecompiler {
public:
explicit ExprDecompiler(GLSLDecompiler& decomp) : decomp{decomp} {}
void operator()(VideoCommon::Shader::ExprAnd& expr) {
inner += "( ";
std::visit(*this, *expr.operand1);
inner += " && ";
std::visit(*this, *expr.operand2);
inner += ')';
}
void operator()(VideoCommon::Shader::ExprOr& expr) {
inner += "( ";
std::visit(*this, *expr.operand1);
inner += " || ";
std::visit(*this, *expr.operand2);
inner += ')';
}
void operator()(VideoCommon::Shader::ExprNot& expr) {
inner += '!';
std::visit(*this, *expr.operand1);
}
void operator()(VideoCommon::Shader::ExprPredicate& expr) {
const auto pred = static_cast<Tegra::Shader::Pred>(expr.predicate);
inner += decomp.GetPredicate(pred);
}
void operator()(VideoCommon::Shader::ExprCondCode& expr) {
const Node cc = decomp.ir.GetConditionCode(expr.cc);
std::string target;
if (const auto pred = std::get_if<PredicateNode>(&*cc)) {
const auto index = pred->GetIndex();
switch (index) {
case Tegra::Shader::Pred::NeverExecute:
target = "false";
case Tegra::Shader::Pred::UnusedIndex:
target = "true";
default:
target = decomp.GetPredicate(index);
}
} else if (const auto flag = std::get_if<InternalFlagNode>(&*cc)) {
target = decomp.GetInternalFlag(flag->GetFlag());
} else {
UNREACHABLE();
}
inner += target;
}
void operator()(VideoCommon::Shader::ExprVar& expr) {
inner += GetFlowVariable(expr.var_index);
}
void operator()(VideoCommon::Shader::ExprBoolean& expr) {
inner += expr.value ? "true" : "false";
}
std::string& GetResult() {
return inner;
}
private:
std::string inner;
GLSLDecompiler& decomp;
};
class ASTDecompiler {
public:
explicit ASTDecompiler(GLSLDecompiler& decomp) : decomp{decomp} {}
void operator()(VideoCommon::Shader::ASTProgram& ast) {
ASTNode current = ast.nodes.GetFirst();
while (current) {
Visit(current);
current = current->GetNext();
}
}
void operator()(VideoCommon::Shader::ASTIfThen& ast) {
ExprDecompiler expr_parser{decomp};
std::visit(expr_parser, *ast.condition);
decomp.code.AddLine("if ({}) {{", expr_parser.GetResult());
decomp.code.scope++;
ASTNode current = ast.nodes.GetFirst();
while (current) {
Visit(current);
current = current->GetNext();
}
decomp.code.scope--;
decomp.code.AddLine("}}");
}
void operator()(VideoCommon::Shader::ASTIfElse& ast) {
decomp.code.AddLine("else {{");
decomp.code.scope++;
ASTNode current = ast.nodes.GetFirst();
while (current) {
Visit(current);
current = current->GetNext();
}
decomp.code.scope--;
decomp.code.AddLine("}}");
}
void operator()(VideoCommon::Shader::ASTBlockEncoded& ast) {
UNREACHABLE();
}
void operator()(VideoCommon::Shader::ASTBlockDecoded& ast) {
decomp.VisitBlock(ast.nodes);
}
void operator()(VideoCommon::Shader::ASTVarSet& ast) {
ExprDecompiler expr_parser{decomp};
std::visit(expr_parser, *ast.condition);
decomp.code.AddLine("{} = {};", GetFlowVariable(ast.index), expr_parser.GetResult());
}
void operator()(VideoCommon::Shader::ASTLabel& ast) {
decomp.code.AddLine("// Label_{}:", ast.index);
}
void operator()(VideoCommon::Shader::ASTGoto& ast) {
UNREACHABLE();
}
void operator()(VideoCommon::Shader::ASTDoWhile& ast) {
ExprDecompiler expr_parser{decomp};
std::visit(expr_parser, *ast.condition);
decomp.code.AddLine("do {{");
decomp.code.scope++;
ASTNode current = ast.nodes.GetFirst();
while (current) {
Visit(current);
current = current->GetNext();
}
decomp.code.scope--;
decomp.code.AddLine("}} while({});", expr_parser.GetResult());
}
void operator()(VideoCommon::Shader::ASTReturn& ast) {
const bool is_true = VideoCommon::Shader::ExprIsTrue(ast.condition);
if (!is_true) {
ExprDecompiler expr_parser{decomp};
std::visit(expr_parser, *ast.condition);
decomp.code.AddLine("if ({}) {{", expr_parser.GetResult());
decomp.code.scope++;
}
if (ast.kills) {
decomp.code.AddLine("discard;");
} else {
decomp.PreExit();
decomp.code.AddLine("return;");
}
if (!is_true) {
decomp.code.scope--;
decomp.code.AddLine("}}");
}
}
void operator()(VideoCommon::Shader::ASTBreak& ast) {
const bool is_true = VideoCommon::Shader::ExprIsTrue(ast.condition);
if (!is_true) {
ExprDecompiler expr_parser{decomp};
std::visit(expr_parser, *ast.condition);
decomp.code.AddLine("if ({}) {{", expr_parser.GetResult());
decomp.code.scope++;
}
decomp.code.AddLine("break;");
if (!is_true) {
decomp.code.scope--;
decomp.code.AddLine("}}");
}
}
void Visit(VideoCommon::Shader::ASTNode& node) {
std::visit(*this, *node->GetInnerData());
}
private:
GLSLDecompiler& decomp;
};
void GLSLDecompiler::DecompileAST() {
const u32 num_flow_variables = ir.GetASTNumVariables();
for (u32 i = 0; i < num_flow_variables; i++) {
code.AddLine("bool {} = false;", GetFlowVariable(i));
}
ASTDecompiler decompiler{*this};
VideoCommon::Shader::ASTNode program = ir.GetASTProgram();
decompiler.Visit(program);
}
} // Anonymous namespace
std::string GetCommonDeclarations() {

View File

@@ -112,14 +112,15 @@ std::optional<std::pair<std::vector<ShaderDiskCacheRaw>, std::vector<ShaderDiskC
ShaderDiskCacheOpenGL::LoadTransferable() {
// Skip games without title id
const bool has_title_id = system.CurrentProcess()->GetTitleID() != 0;
if (!Settings::values.use_disk_shader_cache || !has_title_id)
if (!Settings::values.use_disk_shader_cache || !has_title_id) {
return {};
tried_to_load = true;
}
FileUtil::IOFile file(GetTransferablePath(), "rb");
if (!file.IsOpen()) {
LOG_INFO(Render_OpenGL, "No transferable shader cache found for game with title id={}",
GetTitleID());
is_usable = true;
return {};
}
@@ -135,6 +136,7 @@ ShaderDiskCacheOpenGL::LoadTransferable() {
LOG_INFO(Render_OpenGL, "Transferable shader cache is old - removing");
file.Close();
InvalidateTransferable();
is_usable = true;
return {};
}
if (version > NativeVersion) {
@@ -180,13 +182,15 @@ ShaderDiskCacheOpenGL::LoadTransferable() {
}
}
return {{raws, usages}};
is_usable = true;
return {{std::move(raws), std::move(usages)}};
}
std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>, ShaderDumpsMap>
ShaderDiskCacheOpenGL::LoadPrecompiled() {
if (!IsUsable())
if (!is_usable) {
return {};
}
FileUtil::IOFile file(GetPrecompiledPath(), "rb");
if (!file.IsOpen()) {
@@ -479,8 +483,9 @@ void ShaderDiskCacheOpenGL::InvalidatePrecompiled() {
}
void ShaderDiskCacheOpenGL::SaveRaw(const ShaderDiskCacheRaw& entry) {
if (!IsUsable())
if (!is_usable) {
return;
}
const u64 id = entry.GetUniqueIdentifier();
if (transferable.find(id) != transferable.end()) {
@@ -501,8 +506,9 @@ void ShaderDiskCacheOpenGL::SaveRaw(const ShaderDiskCacheRaw& entry) {
}
void ShaderDiskCacheOpenGL::SaveUsage(const ShaderDiskCacheUsage& usage) {
if (!IsUsable())
if (!is_usable) {
return;
}
const auto it = transferable.find(usage.unique_identifier);
ASSERT_MSG(it != transferable.end(), "Saving shader usage without storing raw previously");
@@ -528,8 +534,9 @@ void ShaderDiskCacheOpenGL::SaveUsage(const ShaderDiskCacheUsage& usage) {
void ShaderDiskCacheOpenGL::SaveDecompiled(u64 unique_identifier, const std::string& code,
const GLShader::ShaderEntries& entries) {
if (!IsUsable())
if (!is_usable) {
return;
}
if (precompiled_cache_virtual_file.GetSize() == 0) {
SavePrecompiledHeaderToVirtualPrecompiledCache();
@@ -543,8 +550,9 @@ void ShaderDiskCacheOpenGL::SaveDecompiled(u64 unique_identifier, const std::str
}
void ShaderDiskCacheOpenGL::SaveDump(const ShaderDiskCacheUsage& usage, GLuint program) {
if (!IsUsable())
if (!is_usable) {
return;
}
GLint binary_length{};
glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &binary_length);
@@ -565,10 +573,6 @@ void ShaderDiskCacheOpenGL::SaveDump(const ShaderDiskCacheUsage& usage, GLuint p
}
}
bool ShaderDiskCacheOpenGL::IsUsable() const {
return tried_to_load && Settings::values.use_disk_shader_cache;
}
FileUtil::IOFile ShaderDiskCacheOpenGL::AppendTransferableFile() const {
if (!EnsureDirectories())
return {};

View File

@@ -224,9 +224,6 @@ private:
bool SaveDecompiledFile(u64 unique_identifier, const std::string& code,
const GLShader::ShaderEntries& entries);
/// Returns if the cache can be used
bool IsUsable() const;
/// Opens current game's transferable file and write it's header if it doesn't exist
FileUtil::IOFile AppendTransferableFile() const;
@@ -297,7 +294,7 @@ private:
std::unordered_map<u64, std::unordered_set<ShaderDiskCacheUsage>> transferable;
// The cache has been loaded at boot
bool tried_to_load{};
bool is_usable{};
};
} // namespace OpenGL

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