Compare commits

...

122 Commits

Author SHA1 Message Date
Lioncash ddcdbce067 gl_buffer_cache: Default initialize member variables
Ensures that the cache always has a deterministic initial state.
2018-09-06 15:07:15 -04:00
Lioncash 8d685a29bc gl_buffer_cache: Make GetHandle() a const member function
GetHandle() internally calls GetHandle() on the stream_buffer instance,
which is a const member function, so this can be made const as well.
2018-09-06 15:07:15 -04:00
Lioncash 14230fe2af gl_buffer_cache: Remove unnecessary includes 2018-09-06 15:05:52 -04:00
Lioncash 68296d9474 gl_buffer_cache: Make constructor explicit
Implicit conversions during construction isn't desirable here.
2018-09-06 14:54:49 -04:00
bunnei fbaefc47a0 Merge pull request #1248 from degasus/shader_fix
gl_shader_gen: Initialize position.
2018-09-06 13:06:09 -04:00
Markus Wick a781042700 gl_shader_gen: Initialize position.
IMO the old code is fine, but nvidia raises shader compiler warnings.
Trivial fix through...
2018-09-06 13:37:50 +02:00
bunnei 77554ac773 Merge pull request #1243 from degasus/VAO_cache
gl_rasterizer: Implement a VAO cache.
2018-09-05 22:50:52 -04:00
bunnei 6f09c5b128 Merge pull request #1244 from FernandoS27/ipa
shader_decompiler: Implemented IPA Properly (Stage 1)
2018-09-05 21:20:40 -04:00
FernandoS27 e63b229f4a Implemented IPA Properly 2018-09-05 20:15:47 -04:00
bunnei 94f193af65 Merge pull request #1242 from lioncash/file-sys
file_sys/submission_package: Replace includes with forward declarations where applicable
2018-09-05 18:53:32 -04:00
bunnei a6ae765410 Merge pull request #1179 from DarkLordZach/bktr
file_sys: Add support for BKTR format (Game Updates)
2018-09-05 18:06:11 -04:00
bunnei aba988f71c Merge pull request #1245 from degasus/optimizations
gl_rasterizer: Skip TODO log.
2018-09-05 16:13:46 -04:00
Markus Wick 7f15306f78 gl_rasterizer: Skip TODO log.
This is called ~3k times per frame in SMO ingame.
My laptop spends ~3ms per frame on allocating and freeing this string.

Let's just stop printing this kind of redundant information.
2018-09-05 20:20:20 +02:00
Lioncash 6bd6beee20 file_sys/submission_package: Correct constructor initialization list order
Orders the elements in the sequence to match the order in which they'll
actually be initialized in.
2018-09-05 13:44:42 -04:00
Markus Wick d3ad9469a1 gl_rasterizer: Implement a VAO cache.
This patch caches VAO objects instead of re-emiting all pointers per draw call.
Configuring this pointers is known as a fast task, but it yields too many GL
calls. So for better performance, just bind the VAO instead of 16 pointers.
2018-09-05 18:46:35 +02:00
Lioncash c0b7ed8b58 file_sys/submission_package: Replace includes with forward declarations where applicable 2018-09-05 12:08:04 -04:00
bunnei 527e362a83 Merge pull request #1217 from degasus/vbo_cache2
renderer_opengl: Implement a buffer cache.
2018-09-05 11:35:31 -04:00
Markus Wick 50a806ea67 renderer_opengl: Implement a buffer cache.
The idea of this cache is to avoid redundant uploads. So we are going
to cache the uploaded buffers within the stream_buffer and just reuse
the old pointers.
The next step is to implement a VBO cache on GPU memory, but for now,
I want to check the overhead of the cache management. Fetching the
buffer over PCI-E should be quite fast.
2018-09-05 08:03:50 +02:00
bunnei a1ef02c3e6 Merge pull request #1240 from degasus/optimizations
gl_shader_cache: Use an u32 for the binding point cache.
2018-09-04 18:20:14 -04:00
Zach Hilman c913136eb2 bktr: Fix bucket overlap error 2018-09-04 17:01:54 -04:00
Zach Hilman 7d5d781b20 drd: Parse title ID from program metadata 2018-09-04 16:25:30 -04:00
Zach Hilman 23a16c1720 patch_manager: Centralize Control-type NCA parsing 2018-09-04 16:25:10 -04:00
Zach Hilman 92e26df00f nsp: Fix error masking issue with XCI files
Now display correct error instead of catch-all MissingProgramNCA
2018-09-04 16:24:24 -04:00
Zach Hilman c91b60a421 game_list: Fix version display on non-NAND titles 2018-09-04 16:24:02 -04:00
Zach Hilman cbd517d8cc bktr: Add logging on successful patch 2018-09-04 16:24:02 -04:00
Zach Hilman 2814ca3624 game_list: Use friendly game versions
Mainly, from control.nacp metadata instead of cnmt metadata
2018-09-04 16:24:02 -04:00
Zach Hilman a6e75cd45b bktr: Implement IVFC offset shifting
Fixes base game read errors
2018-09-04 16:24:02 -04:00
Zach Hilman 9664ce255d bktr: Fix missing includes and optimize style 2018-09-04 16:24:02 -04:00
Zach Hilman f92b3512e0 main: Make game updates installable 2018-09-04 16:24:02 -04:00
Zach Hilman 8e150c46b9 game_list: Display patch names and versions on list 2018-09-04 16:24:02 -04:00
Zach Hilman f5e03b9173 loader: Add BKTR-specific error messages and codes 2018-09-04 16:23:44 -04:00
Zach Hilman 08fcb4694f loader: Ignore patches on NRO and DRD 2018-09-04 16:23:15 -04:00
Zach Hilman 97bf83bc56 patch_manager: Add usages of patches to ExeFS 2018-09-04 16:23:15 -04:00
Zach Hilman 8e900a301a file_sys: Add class to manage game patches
Right now only includes Updates, but should eventually contain all of the other patches we need.
2018-09-04 16:22:25 -04:00
Zach Hilman 54e7ddb93a file_sys: Add BKTR patching mechanism 2018-09-04 16:22:25 -04:00
Zach Hilman 1efe5a76b1 content_archive: Add BKTR header parsing to NCA 2018-09-04 16:22:25 -04:00
Zach Hilman 9951f6d054 registration: Add RegisteredCacheUnion
Aggregates multiple caches into one interface
2018-09-04 16:21:40 -04:00
Zach Hilman d2caf4af7d game_list: Use RegisteredCacheUnion for installed
Reduces code
2018-09-04 16:21:40 -04:00
Zach Hilman 99fbcb3bf2 aes_util: Fix error involving reads of less than 0x10
Issues with block size are fixed by making all reads minimum length of 0x10
2018-09-04 16:21:40 -04:00
bunnei faa9e066ab Merge pull request #1178 from DarkLordZach/nsp
file_sys: Add Nintendo Submissions Package (NSP) file format
2018-09-04 16:20:40 -04:00
Markus Wick 99a71580c4 gl_shader_cache: Use an u32 for the binding point cache.
The std::string generation with its malloc and free requirement
was a noticeable overhead. Also switch to an ordered_map to
avoid the std::hash call. As those maps usually have a size of
two elements, the lookup time shall not matter.
2018-09-04 21:04:41 +02:00
Zach Hilman 87be4bc283 main: Only show DRD deprecation warning once 2018-09-04 14:44:48 -04:00
Zach Hilman e973cceadd control_metadata: Use alternate language names if AmericanEnglish isn't available 2018-09-04 14:30:03 -04:00
Zach Hilman 23d2c50479 card_image: Add program title ID getter 2018-09-04 14:29:19 -04:00
Zach Hilman 1280061725 qt: Add deprecation warnings for DRD format 2018-09-04 14:29:19 -04:00
Zach Hilman 8974771334 registration: Fix NSP installation errors 2018-09-04 14:29:19 -04:00
Zach Hilman e4e55d064e nsp: Comply with style and performance guidelines 2018-09-04 14:29:19 -04:00
Zach Hilman 58473309a0 qt: Add UI support for NSP files 2018-09-04 14:28:41 -04:00
Zach Hilman f7eaea424d registration: Add support for installing NSP files 2018-09-04 14:28:41 -04:00
Zach Hilman d7518cf6e0 loader: Add AppLoader for NSP files 2018-09-04 14:27:33 -04:00
Zach Hilman 5c8aff984e card_image: Parse XCI secure partition with NSP
Eliminated duplicate code and adds support for Rev1+ carts
2018-09-04 14:27:33 -04:00
Zach Hilman 93703431e2 file_sys: Add Nintendo Submission Package (NSP) 2018-09-04 14:25:54 -04:00
Zach Hilman a040929c90 drd: Load title ID from program metadata
Previously only loaded from control metadata
2018-09-04 14:25:54 -04:00
Zach Hilman b555311438 loader: Add NSP file type and NSP-specific errors 2018-09-04 14:25:54 -04:00
Zach Hilman d770c60205 key_manager: Avoid autogeneration if key exists 2018-09-04 14:25:54 -04:00
bunnei dda4b5e89e Merge pull request #1238 from lioncash/explicit
common/logging: Minor changes
2018-09-04 12:18:00 -04:00
bunnei 9a07e9f805 Merge pull request #1237 from degasus/optimizations
Optimizations
2018-09-04 12:16:06 -04:00
bunnei ed37b68fb5 Merge pull request #1223 from DarkLordZach/custom-nand-sd-dirs
file_sys: Allow for custom NAND/SD directories
2018-09-04 11:54:22 -04:00
bunnei 26e96d16d0 Merge pull request #1232 from lioncash/copy
gl_shader_decompiler: Use used_shaders member variable directly within GenerateDeclarations()
2018-09-04 11:52:25 -04:00
bunnei 8ec1e16867 Merge pull request #1235 from lioncash/forward-decl
file_sys: Replace includes with forward declarations where applicable
2018-09-04 11:51:54 -04:00
bunnei 5a29b358aa Merge pull request #1236 from degasus/microprofile
Update microprofile scopes.
2018-09-04 11:50:58 -04:00
bunnei c156ee8eb8 Merge pull request #1230 from lioncash/ssl
ssl: Move SSL class to cpp file
2018-09-04 11:49:22 -04:00
Lioncash 6ef84f1c4c common/logging: Amend documentation comments
Multi-line doc comments still need the '<' after the ///, otherwise it's
treated as a regular comment and makes the original doc comment broken
in viewers, IDEs, etc. While we're at it, also fix some typos in the
comments.
2018-09-04 10:49:08 -04:00
Lioncash 2949d9552c common/logging/filter: Replace C-style case with C++ static_cast 2018-09-04 10:44:36 -04:00
Lioncash 978f3a3282 common/logging/filter: Make constructor explicit
Implicit conversions aren't desirable here.
2018-09-04 10:43:31 -04:00
Markus Wick dce624e3f1 core: Use a raw pointer in GetGPUDebugContext.
This helper is called very often. The memory ownership shall not be transfered, so just return the raw pointer.
2018-09-04 14:10:05 +02:00
Markus Wick 2081ed7db2 command_processor: Use std::array for bound_engines.
subchannel is a 3 bit field. So there must not be more than 8 bound engines.
And using a hashmap for up to 8 values is a bit overpowered.
2018-09-04 14:10:05 +02:00
Markus Wick 10bc725944 Update microprofile scopes.
Blame the subsystems which deserve the blame :)

The updated list is not complete, just the ones I've spotted on random sampling the stack trace.
2018-09-04 11:04:26 +02:00
Lioncash a813c10e1c file_sys: Replace includes with forward declarations where applicable
Cuts down on include dependencies, resulting in less files that need to
be rebuilt when certain things are changed.
2018-09-03 22:52:24 -04:00
bunnei 1c5636e690 Merge pull request #1231 from lioncash/global
service: Migrate global named port map to the KernelCore class
2018-09-03 21:21:12 -04:00
bunnei 2afe8ac4a7 Merge pull request #1229 from lioncash/forward-decl
vfs_real: Forward declare IOFile
2018-09-03 21:20:34 -04:00
Zach Hilman 04397cd185 qt: Add message about not moving contents on dir change 2018-09-03 19:23:33 -04:00
Zach Hilman 1ff3318458 qt: Add UI options to change NAND/SD dirs 2018-09-03 19:23:33 -04:00
Zach Hilman b2268f1f8d settings: Save and load NAND/SD dirs from config 2018-09-03 19:23:33 -04:00
Mat M 9cfe2414cb Merge pull request #1233 from lioncash/dynarmic
externals: Update dynarmic to 0435ac2
2018-09-03 16:22:13 -04:00
Lioncash c6fd56b00f externals: Update dynarmic to 0435ac2 2018-09-03 08:04:24 -04:00
Lioncash 18a89931a9 gl_shader_decompiler: Use used_shaders member variable directly within GenerateDeclarations()
Using the getter function intended for external code here makes an
unnecessary copy of the already-accessible used_shaders vector.
2018-09-02 13:10:11 -04:00
Lioncash a405373144 vfs_real: Forward declare IOFile
Eliminates the need to rebuild some source files if the file_util header
ever changes. This also uncovered some indirect inclusions, which have
also been fixed.
2018-09-02 12:38:14 -04:00
Lioncash 1242c1ec0a service: Migrate global named port map to the KernelCore class
Now that we have a class representing the kernel in some capacity, we
now have a place to put the named port map, so we move it over and get
rid of another piece of global state within the core.
2018-09-02 12:35:30 -04:00
Lioncash 41cd766438 ssl: Move SSL class to cpp file
This isn't required to be visible to anything outside of the main source
file, and will eliminate needing to rebuild anything else including the
header if the SSL class needs to be changed in the future.
2018-09-02 11:45:26 -04:00
bunnei 325f3e0693 Merge pull request #1213 from DarkLordZach/octopath-fs
filesystem/maxwell_3d: Various changes to boot Project Octopath Traveller
2018-09-02 10:49:18 -04:00
bunnei 89be49d2f3 Merge pull request #1215 from ogniK5377/texs-nodep-assert
Added assert for TEXS nodep
2018-09-02 10:48:27 -04:00
bunnei 2714d9e64c Merge pull request #1219 from jroweboy/less-artifacts
Build - Upload fewer artifacts
2018-09-02 10:48:03 -04:00
bunnei d2ade27c3f Merge pull request #1220 from FearlessTobi/extensions-qol
yuzu: Display the unsupported GL extensions in the popup
2018-09-02 10:47:25 -04:00
bunnei 177c45e97d Merge pull request #1214 from ogniK5377/ipa-assert
Added better asserts to IPA, Renamed IPA modes to match mesa
2018-09-02 10:44:43 -04:00
bunnei 9c206fe94d Merge pull request #1216 from ogniK5377/ffma-assert
Added FFMA asserts and missing fields
2018-09-02 10:44:13 -04:00
bunnei 1ccc0457d5 Merge pull request #1218 from ogniK5377/fmul-assert
Added FMUL asserts
2018-09-02 10:43:48 -04:00
bunnei 7a439630bb Merge pull request #1228 from lioncash/construct
filesystem: Move dir retrieval after path checking in DeleteFile()
2018-09-02 10:43:09 -04:00
Lioncash fda8f1da20 filesystem: Move dir retrieval after path checking in DeleteFile()
We don't need to do the lookup if the path is considered empty
currently.
2018-09-02 09:20:17 -04:00
fearlessTobi 0f453488e2 citra_qt: Display the unsupported GL extensions in the popup 2018-09-01 19:01:53 +02:00
James Rowe a0e1fbfe14 Build - Upload fewer artifacts
Appveyor has a limit on artifact retention, and we hit the limit all the
time, so just lower the number of build artifacts to just the final zip
2018-09-01 10:42:16 -06:00
David Marcec 60754b4728 Removed saturate assert
Unneeded as we already implement it
2018-09-01 19:33:32 +10:00
David Marcec 2edab4e840 Removed saturate assert
Saturate already implemented
2018-09-01 19:29:20 +10:00
David Marcec 2bc6abb9a1 Changed tab5980_0 default from 0 -> 1 2018-09-01 19:15:03 +10:00
David Marcec 6f8ed9508d Added FMUL asserts 2018-09-01 19:05:10 +10:00
David Marcec b89fc407d7 Added FFMA asserts 2018-09-01 18:45:14 +10:00
David Marcec 948bc87a59 Added assert for TEXS nodep 2018-09-01 17:00:01 +10:00
David Marcec ad3dca7e62 Added better asserts to IPA, Renamed IPA modes to match mesa
IpaMode is changed to IpaInterpMode
IpaMode is suppose to be 2 bits not 3
Added IpaSampleMode
Added Saturate

Renamed modes based on
https://github.com/mesa3d/mesa/blob/d27c7918916cdc8092959124955f887592e37d72/src/gallium/drivers/nouveau/codegen/nv50_ir_emit_gm107.cpp#L2530
2018-09-01 16:34:27 +10:00
Zach Hilman f32e28c7b8 maxwell_3d: Use CoreTiming for query timestamp 2018-08-31 23:25:18 -04:00
Zach Hilman 19d0951ae6 filesystem: Implement OpenReadOnlySaveDataFilesystem 2018-08-31 23:19:49 -04:00
Zach Hilman 7939ea18e8 filesystem: Add OpenFileSystemWithPatch 2018-08-31 23:19:23 -04:00
bunnei c69dc5acf9 Merge pull request #1196 from FearlessTobi/ccache-consistency
.travis: Use Citras ccache for builds instead of yuzus
2018-08-31 21:50:44 -04:00
bunnei 1c05c06e04 Merge pull request #1212 from lioncash/forward-decl
core/core: Replace includes with forward declarations where applicable
2018-08-31 21:50:12 -04:00
Lioncash 4a587b81b2 core/core: Replace includes with forward declarations where applicable
The follow-up to e2457418da, which
replaces most of the includes in the core header with forward declarations.

This makes it so that if any of the headers the core header was
previously including change, then no one will need to rebuild the bulk
of the core, due to core.h being quite a prevalent inclusion.

This should make turnaround for changes much faster for developers.
2018-08-31 16:30:14 -04:00
fearlessTobi dc3cc0002c travis: use Citras ccache 2018-08-31 20:13:26 +02:00
bunnei 42588493d5 Merge pull request #1205 from bunnei/improve-rasterizer-cache-2
Various fixes and improvements to rasterizer cache 2: Electric Boogaloo
2018-08-31 13:24:21 -04:00
bunnei 7f7eb29323 gl_rasterizer_cache: Use accurate framebuffer setting for accurate copies. 2018-08-31 13:07:28 -04:00
bunnei 123c065086 gl_rasterizer_cache: Also use reserve cache for RecreateSurface. 2018-08-31 13:07:28 -04:00
bunnei 9bc71fcc5f rasterizer_cache: Use boost::interval_map for a more accurate cache. 2018-08-31 13:07:28 -04:00
bunnei d647d9550c gl_renderer: Cache textures, framebuffers, and shaders based on CPU address. 2018-08-31 13:07:27 -04:00
bunnei 16d65182f9 gl_rasterizer: Fix issues with the rasterizer cache.
- Use a single cached page map.
- Fix calculation of ending page.
2018-08-31 13:07:27 -04:00
greggameplayer 06578e89b2 Implement BC6H_UF16 & BC6H_SF16 (#1092)
* Implement BC6H_UF16 & BC6H_SF16
Require by ARMS

* correct coding style

* correct coding style part 2
2018-08-31 12:11:19 -04:00
bunnei f08d24e9c0 Merge pull request #1204 from lioncash/pimpl
core: Make the main System class use the PImpl idiom
2018-08-31 11:31:20 -04:00
bunnei 6683bf50b5 Merge pull request #1207 from degasus/hotfix
Report correct shader size.
2018-08-31 11:21:15 -04:00
bunnei e205e74e1f Merge pull request #1208 from Hexagon12/pred-comp-14
Add predicate comparison 14 (GreaterEqualWithNan)
2018-08-31 11:20:47 -04:00
Markus Wick 5be8b7a362 Report correct shader size.
Seems like this was an oversee in regards to 1fd979f50a
It changed GLShader::ProgramCode to a std::vector, so sizeof is wrong.
2018-08-31 09:56:37 +02:00
Hexagon12 d626bc8c62 Added predicate comparison GreaterEqualWithNan 2018-08-31 10:40:18 +03:00
bunnei 26aaa86ece Merge pull request #1195 from FearlessTobi/port-gamelist-compat
yuzu: Show game compatibility in the game list (PR ported from Citra)
2018-08-30 21:34:43 -04:00
Laku 915ab81ec2 gl_shader_decompiler: Implement POPC (#1203)
* Implement POPC

* implement invert
2018-08-30 21:32:58 -04:00
bunnei d6accf96ff Merge pull request #1200 from bunnei/improve-ipa
gl_shader_decompiler: Improve IPA for Pass mode with Position attribute.
2018-08-30 10:31:26 -04:00
fearlessTobi 78653f7339 Show game compatibility within yuzu 2018-08-29 15:42:53 +02:00
bunnei b1ccd88434 gl_shader_decompiler: Improve IPA for Pass mode with Position attribute. 2018-08-29 00:37:29 -04:00
120 changed files with 3221 additions and 839 deletions
+2 -4
View File
@@ -20,6 +20,7 @@ matrix:
install: "./.travis/linux/deps.sh"
script: "./.travis/linux/build.sh"
after_success: "./.travis/linux/upload.sh"
cache: ccache
- os: osx
env: NAME="macos build"
sudo: false
@@ -27,6 +28,7 @@ matrix:
install: "./.travis/macos/deps.sh"
script: "./.travis/macos/build.sh"
after_success: "./.travis/macos/upload.sh"
cache: ccache
deploy:
provider: releases
@@ -42,7 +44,3 @@ notifications:
webhooks:
urls:
- https://api.yuzu-emu.org/code/travis/notify
cache:
directories:
- $HOME/.ccache
+2 -1
View File
@@ -1,3 +1,4 @@
#!/bin/bash -ex
docker run -e CCACHE_DIR=/ccache -v $HOME/.ccache:/ccache --env-file .travis/common/travis-ci.env -v $(pwd):/yuzu ubuntu:18.04 /bin/bash /yuzu/.travis/linux/docker.sh
mkdir -p "$HOME/.ccache"
docker run --env-file .travis/common/travis-ci.env -v $(pwd):/yuzu -v "$HOME/.ccache":/root/.ccache ubuntu:18.04 /bin/bash /yuzu/.travis/linux/docker.sh
+1 -7
View File
@@ -5,14 +5,8 @@ apt-get install --no-install-recommends -y build-essential git libqt5opengl5-dev
cd /yuzu
export PATH=/usr/lib/ccache:$PATH
ln -sf /usr/bin/ccache /usr/lib/ccache/cc
ln -sf /usr/bin/ccache /usr/lib/ccache/c++
mkdir build && cd build
ccache --show-stats > ccache_before
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -G Ninja
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -G Ninja
ninja
ccache --show-stats > ccache_after
diff -U100 ccache_before ccache_after || true
ctest -VV -C Release
+2 -5
View File
@@ -5,14 +5,11 @@ set -o pipefail
export MACOSX_DEPLOYMENT_TARGET=10.12
export Qt5_DIR=$(brew --prefix)/opt/qt5
export UNICORNDIR=$(pwd)/externals/unicorn
export PATH="/usr/local/opt/ccache/libexec:$PATH"
mkdir build && cd build
export PATH=/usr/local/opt/ccache/libexec:$PATH
ccache --show-stats > ccache_before
cmake --version
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON
make -j4
ccache --show-stats > ccache_after
diff -U100 ccache_before ccache_after || true
ctest -VV -C Release
+13
View File
@@ -41,6 +41,19 @@ function(check_submodules_present)
endfunction()
check_submodules_present()
configure_file(${CMAKE_SOURCE_DIR}/dist/compatibility_list/compatibility_list.qrc
${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
COPYONLY)
if (ENABLE_COMPATIBILITY_LIST_DOWNLOAD AND NOT EXISTS ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
message(STATUS "Downloading compatibility list for yuzu...")
file(DOWNLOAD
https://api.yuzu-emu.org/gamedb/
"${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json" SHOW_PROGRESS)
endif()
if (NOT EXISTS ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
file(WRITE ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json "")
endif()
# Detect current compilation architecture and create standard definitions
# =======================================================================
+2 -6
View File
@@ -41,9 +41,9 @@ before_build:
- ps: |
if ($env:BUILD_TYPE -eq 'msvc') {
# redirect stderr and change the exit code to prevent powershell from cancelling the build if cmake prints a warning
cmd /C 'cmake -G "Visual Studio 15 2017 Win64" -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_BUNDLED_UNICORN=1 .. 2>&1 && exit 0'
cmd /C 'cmake -G "Visual Studio 15 2017 Win64" -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_BUNDLED_UNICORN=1 -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON .. 2>&1 && exit 0'
} else {
C:\msys64\usr\bin\bash.exe -lc "cmake -G 'MSYS Makefiles' -DYUZU_BUILD_UNICORN=1 -DCMAKE_BUILD_TYPE=Release .. 2>&1"
C:\msys64\usr\bin\bash.exe -lc "cmake -G 'MSYS Makefiles' -DYUZU_BUILD_UNICORN=1 -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON .. 2>&1"
}
- cd ..
@@ -162,10 +162,6 @@ artifacts:
- path: $(BUILD_ZIP)
name: build
type: zip
- path: $(BUILD_SYMBOLS)
name: debugsymbols
- path: $(BUILD_UPDATE)
name: update
deploy:
provider: GitHub
+5
View File
@@ -0,0 +1,5 @@
<RCC>
<qresource prefix="compatibility_list">
<file>compatibility_list.json</file>
</qresource>
</RCC>
+4
View File
@@ -10,6 +10,7 @@
#include "audio_core/stream.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/microprofile.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
#include "core/settings.h"
@@ -94,7 +95,10 @@ void Stream::PlayNextBuffer() {
CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {});
}
MICROPROFILE_DEFINE(AudioOutput, "Audio", "ReleaseActiveBuffer", MP_RGB(100, 100, 255));
void Stream::ReleaseActiveBuffer() {
MICROPROFILE_SCOPE(AudioOutput);
ASSERT(active_buffer);
released_buffers.push(std::move(active_buffer));
release_callback();
+2 -2
View File
@@ -19,7 +19,7 @@ namespace Log {
class Filter {
public:
/// Initializes the filter with all classes having `default_level` as the minimum level.
Filter(Level default_level = Level::Info);
explicit Filter(Level default_level = Level::Info);
/// Resets the filter so that all classes have `level` as the minimum displayed level.
void ResetAll(Level level);
@@ -49,6 +49,6 @@ public:
bool IsDebug() const;
private:
std::array<Level, (size_t)Class::Count> class_levels;
std::array<Level, static_cast<size_t>(Class::Count)> class_levels;
};
} // namespace Log
+5 -5
View File
@@ -12,14 +12,14 @@ namespace Log {
/// Specifies the severity or level of detail of the log message.
enum class Level : u8 {
Trace, ///< Extremely detailed and repetitive debugging information that is likely to
/// pollute logs.
///< pollute logs.
Debug, ///< Less detailed debugging information.
Info, ///< Status information from important points during execution.
Warning, ///< Minor or potential problems found during execution of a task.
Error, ///< Major problems found during execution of a task that prevent it from being
/// completed.
Critical, ///< Major problems during execution that threathen the stability of the entire
/// application.
///< completed.
Critical, ///< Major problems during execution that threaten the stability of the entire
///< application.
Count ///< Total number of logging levels
};
@@ -49,7 +49,7 @@ enum class Class : ClassType {
Kernel, ///< The HLE implementation of the CTR kernel
Kernel_SVC, ///< Kernel system calls
Service, ///< HLE implementation of system services. Each major service
/// should have its own subclass.
///< should have its own subclass.
Service_ACC, ///< The ACC (Accounts) service
Service_AM, ///< The AM (Applet manager) service
Service_AOC, ///< The AOC (AddOn Content) service
+1 -1
View File
@@ -15,6 +15,6 @@ struct Entry;
std::string FormatLogMessage(const Entry& entry);
/// Formats and prints a log entry to stderr.
void PrintMessage(const Entry& entry);
/// Prints the same message as `PrintMessage`, but colored acoording to the severity level.
/// Prints the same message as `PrintMessage`, but colored according to the severity level.
void PrintColoredMessage(const Entry& entry);
} // namespace Log
+8
View File
@@ -35,8 +35,12 @@ add_library(core STATIC
file_sys/mode.h
file_sys/nca_metadata.cpp
file_sys/nca_metadata.h
file_sys/nca_patch.cpp
file_sys/nca_patch.h
file_sys/partition_filesystem.cpp
file_sys/partition_filesystem.h
file_sys/patch_manager.cpp
file_sys/patch_manager.h
file_sys/program_metadata.cpp
file_sys/program_metadata.h
file_sys/registered_cache.cpp
@@ -49,6 +53,8 @@ add_library(core STATIC
file_sys/savedata_factory.h
file_sys/sdmc_factory.cpp
file_sys/sdmc_factory.h
file_sys/submission_package.cpp
file_sys/submission_package.h
file_sys/vfs.cpp
file_sys/vfs.h
file_sys/vfs_concat.cpp
@@ -359,6 +365,8 @@ add_library(core STATIC
loader/nro.h
loader/nso.cpp
loader/nso.h
loader/nsp.cpp
loader/nsp.h
loader/xci.cpp
loader/xci.h
memory.cpp
+5
View File
@@ -7,8 +7,10 @@
#include <dynarmic/A64/a64.h>
#include <dynarmic/A64/config.h>
#include "common/logging/log.h"
#include "common/microprofile.h"
#include "core/arm/dynarmic/arm_dynarmic.h"
#include "core/core.h"
#include "core/core_cpu.h"
#include "core/core_timing.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/svc.h"
@@ -142,7 +144,10 @@ std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const {
return std::make_unique<Dynarmic::A64::Jit>(config);
}
MICROPROFILE_DEFINE(ARM_Jit_Dynarmic, "ARM JIT", "Dynarmic", MP_RGB(255, 64, 64));
void ARM_Dynarmic::Run() {
MICROPROFILE_SCOPE(ARM_Jit_Dynarmic);
ASSERT(Memory::GetCurrentPageTable() == current_page_table);
jit->Run();
+2 -2
View File
@@ -193,10 +193,10 @@ void ARM_Unicorn::Step() {
ExecuteInstructions(1);
}
MICROPROFILE_DEFINE(ARM_Jit, "ARM JIT", "ARM JIT", MP_RGB(255, 64, 64));
MICROPROFILE_DEFINE(ARM_Jit_Unicorn, "ARM JIT", "Unicorn", MP_RGB(255, 64, 64));
void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
MICROPROFILE_SCOPE(ARM_Jit);
MICROPROFILE_SCOPE(ARM_Jit_Unicorn);
CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions));
CoreTiming::AddTicks(num_instructions);
if (GDBStub::IsServerEnabled()) {
+20 -8
View File
@@ -2,24 +2,36 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <array>
#include <map>
#include <memory>
#include <thread>
#include <utility>
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/arm/exclusive_monitor.h"
#include "core/core.h"
#include "core/core_cpu.h"
#include "core/core_timing.h"
#include "core/file_sys/mode.h"
#include "core/file_sys/vfs_concat.h"
#include "core/file_sys/vfs_real.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/controller.h"
#include "core/hle/service/sm/sm.h"
#include "core/loader/loader.h"
#include "core/perf_stats.h"
#include "core/settings.h"
#include "file_sys/vfs_concat.h"
#include "file_sys/vfs_real.h"
#include "core/telemetry_session.h"
#include "video_core/debug_utils/debug_utils.h"
#include "video_core/gpu.h"
#include "video_core/renderer_base.h"
#include "video_core/video_core.h"
@@ -258,7 +270,7 @@ struct System::Impl {
}
}
PerfStats::Results GetAndResetPerfStats() {
PerfStatsResults GetAndResetPerfStats() {
return perf_stats.GetAndResetStats(CoreTiming::GetGlobalTimeUs());
}
@@ -326,7 +338,7 @@ void System::PrepareReschedule() {
CurrentCpuCore().PrepareReschedule();
}
PerfStats::Results System::GetAndResetPerfStats() {
PerfStatsResults System::GetAndResetPerfStats() {
return impl->GetAndResetPerfStats();
}
@@ -429,15 +441,15 @@ void System::SetGPUDebugContext(std::shared_ptr<Tegra::DebugContext> context) {
impl->debug_context = std::move(context);
}
std::shared_ptr<Tegra::DebugContext> System::GetGPUDebugContext() const {
return impl->debug_context;
Tegra::DebugContext* System::GetGPUDebugContext() const {
return impl->debug_context.get();
}
void System::SetFilesystem(FileSys::VirtualFilesystem vfs) {
void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) {
impl->virtual_filesystem = std::move(vfs);
}
FileSys::VirtualFilesystem System::GetFilesystem() const {
std::shared_ptr<FileSys::VfsFilesystem> System::GetFilesystem() const {
return impl->virtual_filesystem;
}
+36 -22
View File
@@ -4,41 +4,55 @@
#pragma once
#include <array>
#include <map>
#include <cstddef>
#include <memory>
#include <string>
#include <thread>
#include "common/common_types.h"
#include "core/arm/exclusive_monitor.h"
#include "core/core_cpu.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/scheduler.h"
#include "core/loader/loader.h"
#include "core/memory.h"
#include "core/perf_stats.h"
#include "core/telemetry_session.h"
#include "file_sys/vfs_real.h"
#include "hle/service/filesystem/filesystem.h"
#include "video_core/debug_utils/debug_utils.h"
#include "video_core/gpu.h"
namespace Core::Frontend {
class EmuWindow;
}
} // namespace Core::Frontend
namespace FileSys {
class VfsFilesystem;
} // namespace FileSys
namespace Kernel {
class KernelCore;
class Process;
class Scheduler;
} // namespace Kernel
namespace Loader {
class AppLoader;
enum class ResultStatus : u16;
} // namespace Loader
namespace Service::SM {
class ServiceManager;
}
} // namespace Service::SM
namespace Tegra {
class DebugContext;
class GPU;
} // namespace Tegra
namespace VideoCore {
class RendererBase;
}
} // namespace VideoCore
namespace Core {
class ARM_Interface;
class Cpu;
class ExclusiveMonitor;
class FrameLimiter;
class PerfStats;
class TelemetrySession;
struct PerfStatsResults;
class System {
public:
@@ -125,7 +139,7 @@ public:
void PrepareReschedule();
/// Gets and resets core performance statistics
PerfStats::Results GetAndResetPerfStats();
PerfStatsResults GetAndResetPerfStats();
/// Gets an ARM interface to the CPU core that is currently running
ARM_Interface& CurrentArmInterface();
@@ -195,11 +209,11 @@ public:
void SetGPUDebugContext(std::shared_ptr<Tegra::DebugContext> context);
std::shared_ptr<Tegra::DebugContext> GetGPUDebugContext() const;
Tegra::DebugContext* GetGPUDebugContext() const;
void SetFilesystem(FileSys::VirtualFilesystem vfs);
void SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs);
FileSys::VirtualFilesystem GetFilesystem() const;
std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const;
private:
System();
+14
View File
@@ -82,11 +82,25 @@ void AESCipher<Key, KeySize>::Transcode(const u8* src, size_t size, u8* dest, Op
}
} else {
const auto block_size = mbedtls_cipher_get_block_size(context);
if (size < block_size) {
std::vector<u8> block(block_size);
std::memcpy(block.data(), src, size);
Transcode(block.data(), block.size(), block.data(), op);
std::memcpy(dest, block.data(), size);
return;
}
for (size_t offset = 0; offset < size; offset += block_size) {
auto length = std::min<size_t>(block_size, size - offset);
mbedtls_cipher_update(context, src + offset, length, dest + offset, &written);
if (written != length) {
if (length < block_size) {
std::vector<u8> block(block_size);
std::memcpy(block.data(), src + offset, length);
Transcode(block.data(), block.size(), block.data(), op);
std::memcpy(dest + offset, block.data(), length);
return;
}
LOG_WARNING(Crypto, "Not all data was decrypted requested={:016X}, actual={:016X}.",
length, written);
}
+1 -1
View File
@@ -21,7 +21,7 @@ size_t CTREncryptionLayer::Read(u8* data, size_t length, size_t offset) const {
UpdateIV(base_offset + offset);
std::vector<u8> raw = base->ReadBytes(length, offset);
cipher.Transcode(raw.data(), raw.size(), data, Op::Decrypt);
return raw.size();
return length;
}
// offset does not fall on block boundary (0x10)
+16 -3
View File
@@ -8,12 +8,15 @@
#include <locale>
#include <sstream>
#include <string_view>
#include <tuple>
#include <vector>
#include "common/common_paths.h"
#include "common/file_util.h"
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "core/crypto/aes_util.h"
#include "core/crypto/key_manager.h"
#include "core/loader/loader.h"
#include "core/settings.h"
namespace Core::Crypto {
@@ -228,18 +231,28 @@ void KeyManager::WriteKeyToFile(bool title_key, std::string_view keyname,
}
void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
const auto iter = std::find_if(
if (s128_keys.find({id, field1, field2}) != s128_keys.end())
return;
if (id == S128KeyType::Titlekey) {
Key128 rights_id;
std::memcpy(rights_id.data(), &field2, sizeof(u64));
std::memcpy(rights_id.data() + sizeof(u64), &field1, sizeof(u64));
WriteKeyToFile(true, Common::HexArrayToString(rights_id), key);
}
const auto iter2 = std::find_if(
s128_file_id.begin(), s128_file_id.end(),
[&id, &field1, &field2](const std::pair<std::string, KeyIndex<S128KeyType>> elem) {
return std::tie(elem.second.type, elem.second.field1, elem.second.field2) ==
std::tie(id, field1, field2);
});
if (iter != s128_file_id.end())
WriteKeyToFile(id == S128KeyType::Titlekey, iter->first, key);
if (iter2 != s128_file_id.end())
WriteKeyToFile(false, iter2->first, key);
s128_keys[{id, field1, field2}] = key;
}
void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
if (s256_keys.find({id, field1, field2}) != s256_keys.end())
return;
const auto iter = std::find_if(
s256_file_id.begin(), s256_file_id.end(),
[&id, &field1, &field2](const std::pair<std::string, KeyIndex<S256KeyType>> elem) {
+7 -4
View File
@@ -6,16 +6,19 @@
#include <array>
#include <string>
#include <string_view>
#include <type_traits>
#include <vector>
#include <boost/container/flat_map.hpp>
#include <boost/optional.hpp>
#include <fmt/format.h>
#include "common/common_types.h"
#include "core/loader/loader.h"
namespace Loader {
enum class ResultStatus : u16;
}
namespace Core::Crypto {
constexpr u64 TICKET_FILE_TITLEKEY_OFFSET = 0x180;
using Key128 = std::array<u8, 0x10>;
using Key256 = std::array<u8, 0x20>;
using SHA256Hash = std::array<u8, 0x20>;
+3
View File
@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include "core/file_sys/bis_factory.h"
#include "core/file_sys/registered_cache.h"
namespace FileSys {
@@ -13,6 +14,8 @@ BISFactory::BISFactory(VirtualDir nand_root_)
usrnand_cache(std::make_shared<RegisteredCache>(
GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))) {}
BISFactory::~BISFactory() = default;
std::shared_ptr<RegisteredCache> BISFactory::GetSystemNANDContents() const {
return sysnand_cache;
}
+5 -2
View File
@@ -5,17 +5,20 @@
#pragma once
#include <memory>
#include "core/loader/loader.h"
#include "registered_cache.h"
#include "core/file_sys/vfs.h"
namespace FileSys {
class RegisteredCache;
/// File system interface to the Built-In Storage
/// This is currently missing accessors to BIS partitions, but seemed like a good place for the NAND
/// registered caches.
class BISFactory {
public:
explicit BISFactory(VirtualDir nand_root);
~BISFactory();
std::shared_ptr<RegisteredCache> GetSystemNANDContents() const;
std::shared_ptr<RegisteredCache> GetUserNANDContents() const;
+34 -7
View File
@@ -9,7 +9,10 @@
#include "common/logging/log.h"
#include "core/file_sys/card_image.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/partition_filesystem.h"
#include "core/file_sys/submission_package.h"
#include "core/file_sys/vfs_offset.h"
#include "core/loader/loader.h"
@@ -43,15 +46,19 @@ XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) {
partitions[static_cast<size_t>(partition)] = std::make_shared<PartitionFilesystem>(raw);
}
program_nca_status = Loader::ResultStatus::ErrorXCIMissingProgramNCA;
secure_partition = std::make_shared<NSP>(
main_hfs.GetFile(partition_names[static_cast<size_t>(XCIPartition::Secure)]));
auto result = AddNCAFromPartition(XCIPartition::Secure);
if (result != Loader::ResultStatus::Success) {
status = result;
return;
}
const auto secure_ncas = secure_partition->GetNCAsCollapsed();
std::copy(secure_ncas.begin(), secure_ncas.end(), std::back_inserter(ncas));
result = AddNCAFromPartition(XCIPartition::Update);
program =
secure_partition->GetNCA(secure_partition->GetProgramTitleID(), ContentRecordType::Program);
program_nca_status = secure_partition->GetProgramStatus(secure_partition->GetProgramTitleID());
if (program_nca_status == Loader::ResultStatus::ErrorNSPMissingProgramNCA)
program_nca_status = Loader::ResultStatus::ErrorXCIMissingProgramNCA;
auto result = AddNCAFromPartition(XCIPartition::Update);
if (result != Loader::ResultStatus::Success) {
status = result;
return;
@@ -74,6 +81,8 @@ XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) {
status = Loader::ResultStatus::Success;
}
XCI::~XCI() = default;
Loader::ResultStatus XCI::GetStatus() const {
return status;
}
@@ -86,6 +95,10 @@ VirtualDir XCI::GetPartition(XCIPartition partition) const {
return partitions[static_cast<size_t>(partition)];
}
std::shared_ptr<NSP> XCI::GetSecurePartitionNSP() const {
return secure_partition;
}
VirtualDir XCI::GetSecurePartition() const {
return GetPartition(XCIPartition::Secure);
}
@@ -102,6 +115,20 @@ VirtualDir XCI::GetLogoPartition() const {
return GetPartition(XCIPartition::Logo);
}
u64 XCI::GetProgramTitleID() const {
return secure_partition->GetProgramTitleID();
}
std::shared_ptr<NCA> XCI::GetProgramNCA() const {
return program;
}
VirtualFile XCI::GetProgramNCAFile() const {
if (GetProgramNCA() == nullptr)
return nullptr;
return GetProgramNCA()->GetBaseFile();
}
const std::vector<std::shared_ptr<NCA>>& XCI::GetNCAs() const {
return ncas;
}
+17 -2
View File
@@ -5,15 +5,22 @@
#pragma once
#include <array>
#include <memory>
#include <vector>
#include "common/common_types.h"
#include "common/swap.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/vfs.h"
#include "core/loader/loader.h"
namespace Loader {
enum class ResultStatus : u16;
}
namespace FileSys {
class NCA;
enum class NCAContentType : u8;
class NSP;
enum class GamecardSize : u8 {
S_1GB = 0xFA,
S_2GB = 0xF8,
@@ -57,6 +64,7 @@ enum class XCIPartition : u8 { Update, Normal, Secure, Logo };
class XCI : public ReadOnlyVfsDirectory {
public:
explicit XCI(VirtualFile file);
~XCI() override;
Loader::ResultStatus GetStatus() const;
Loader::ResultStatus GetProgramNCAStatus() const;
@@ -64,11 +72,16 @@ public:
u8 GetFormatVersion() const;
VirtualDir GetPartition(XCIPartition partition) const;
std::shared_ptr<NSP> GetSecurePartitionNSP() const;
VirtualDir GetSecurePartition() const;
VirtualDir GetNormalPartition() const;
VirtualDir GetUpdatePartition() const;
VirtualDir GetLogoPartition() const;
u64 GetProgramTitleID() const;
std::shared_ptr<NCA> GetProgramNCA() const;
VirtualFile GetProgramNCAFile() const;
const std::vector<std::shared_ptr<NCA>>& GetNCAs() const;
std::shared_ptr<NCA> GetNCAByType(NCAContentType type) const;
VirtualFile GetNCAFileByType(NCAContentType type) const;
@@ -94,6 +107,8 @@ private:
Loader::ResultStatus program_nca_status;
std::vector<VirtualDir> partitions;
std::shared_ptr<NSP> secure_partition;
std::shared_ptr<NCA> program;
std::vector<std::shared_ptr<NCA>> ncas;
};
} // namespace FileSys
+167 -16
View File
@@ -3,12 +3,17 @@
// Refer to the license.txt file included.
#include <algorithm>
#include <cstring>
#include <utility>
#include <boost/optional.hpp>
#include "common/logging/log.h"
#include "core/crypto/aes_util.h"
#include "core/crypto/ctr_encryption_layer.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/nca_patch.h"
#include "core/file_sys/partition_filesystem.h"
#include "core/file_sys/romfs.h"
#include "core/file_sys/vfs_offset.h"
#include "core/loader/loader.h"
@@ -64,10 +69,31 @@ struct RomFSSuperblock {
};
static_assert(sizeof(RomFSSuperblock) == 0x200, "RomFSSuperblock has incorrect size.");
struct BKTRHeader {
u64_le offset;
u64_le size;
u32_le magic;
INSERT_PADDING_BYTES(0x4);
u32_le number_entries;
INSERT_PADDING_BYTES(0x4);
};
static_assert(sizeof(BKTRHeader) == 0x20, "BKTRHeader has incorrect size.");
struct BKTRSuperblock {
NCASectionHeaderBlock header_block;
IVFCHeader ivfc;
INSERT_PADDING_BYTES(0x18);
BKTRHeader relocation;
BKTRHeader subsection;
INSERT_PADDING_BYTES(0xC0);
};
static_assert(sizeof(BKTRSuperblock) == 0x200, "BKTRSuperblock has incorrect size.");
union NCASectionHeader {
NCASectionRaw raw;
PFS0Superblock pfs0;
RomFSSuperblock romfs;
BKTRSuperblock bktr;
};
static_assert(sizeof(NCASectionHeader) == 0x200, "NCASectionHeader has incorrect size.");
@@ -100,7 +126,7 @@ boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType ty
Core::Crypto::Key128 out;
if (type == NCASectionCryptoType::XTS)
std::copy(key_area.begin(), key_area.begin() + 0x10, out.begin());
else if (type == NCASectionCryptoType::CTR)
else if (type == NCASectionCryptoType::CTR || type == NCASectionCryptoType::BKTR)
std::copy(key_area.begin() + 0x20, key_area.begin() + 0x30, out.begin());
else
LOG_CRITICAL(Crypto, "Called GetKeyAreaKey on invalid NCASectionCryptoType type={:02X}",
@@ -150,6 +176,9 @@ VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting
LOG_DEBUG(Crypto, "called with mode=NONE");
return in;
case NCASectionCryptoType::CTR:
// During normal BKTR decryption, this entire function is skipped. This is for the metadata,
// which uses the same CTR as usual.
case NCASectionCryptoType::BKTR:
LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset);
{
boost::optional<Core::Crypto::Key128> key = boost::none;
@@ -186,7 +215,9 @@ VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting
}
}
NCA::NCA(VirtualFile file_) : file(std::move(file_)) {
NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset)
: file(std::move(file_)),
bktr_base_romfs(bktr_base_romfs_ ? std::move(bktr_base_romfs_) : nullptr) {
status = Loader::ResultStatus::Success;
if (file == nullptr) {
@@ -261,22 +292,21 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) {
is_update = std::find_if(sections.begin(), sections.end(), [](const NCASectionHeader& header) {
return header.raw.header.crypto_type == NCASectionCryptoType::BKTR;
}) != sections.end();
ivfc_offset = 0;
for (std::ptrdiff_t i = 0; i < number_sections; ++i) {
auto section = sections[i];
if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) {
const size_t romfs_offset =
header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER +
section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
const size_t base_offset =
header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER;
ivfc_offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
const size_t romfs_offset = base_offset + ivfc_offset;
const size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size;
auto dec =
Decrypt(section, std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset),
romfs_offset);
if (dec != nullptr) {
files.push_back(std::move(dec));
romfs = files.back();
} else {
auto raw = std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset);
auto dec = Decrypt(section, raw, romfs_offset);
if (dec == nullptr) {
if (status != Loader::ResultStatus::Success)
return;
if (has_rights_id)
@@ -285,6 +315,117 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) {
status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
return;
}
if (section.raw.header.crypto_type == NCASectionCryptoType::BKTR) {
if (section.bktr.relocation.magic != Common::MakeMagic('B', 'K', 'T', 'R') ||
section.bktr.subsection.magic != Common::MakeMagic('B', 'K', 'T', 'R')) {
status = Loader::ResultStatus::ErrorBadBKTRHeader;
return;
}
if (section.bktr.relocation.offset + section.bktr.relocation.size !=
section.bktr.subsection.offset) {
status = Loader::ResultStatus::ErrorBKTRSubsectionNotAfterRelocation;
return;
}
const u64 size =
MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset -
header.section_tables[i].media_offset);
if (section.bktr.subsection.offset + section.bktr.subsection.size != size) {
status = Loader::ResultStatus::ErrorBKTRSubsectionNotAtEnd;
return;
}
const u64 offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
RelocationBlock relocation_block{};
if (dec->ReadObject(&relocation_block, section.bktr.relocation.offset - offset) !=
sizeof(RelocationBlock)) {
status = Loader::ResultStatus::ErrorBadRelocationBlock;
return;
}
SubsectionBlock subsection_block{};
if (dec->ReadObject(&subsection_block, section.bktr.subsection.offset - offset) !=
sizeof(RelocationBlock)) {
status = Loader::ResultStatus::ErrorBadSubsectionBlock;
return;
}
std::vector<RelocationBucketRaw> relocation_buckets_raw(
(section.bktr.relocation.size - sizeof(RelocationBlock)) /
sizeof(RelocationBucketRaw));
if (dec->ReadBytes(relocation_buckets_raw.data(),
section.bktr.relocation.size - sizeof(RelocationBlock),
section.bktr.relocation.offset + sizeof(RelocationBlock) -
offset) !=
section.bktr.relocation.size - sizeof(RelocationBlock)) {
status = Loader::ResultStatus::ErrorBadRelocationBuckets;
return;
}
std::vector<SubsectionBucketRaw> subsection_buckets_raw(
(section.bktr.subsection.size - sizeof(SubsectionBlock)) /
sizeof(SubsectionBucketRaw));
if (dec->ReadBytes(subsection_buckets_raw.data(),
section.bktr.subsection.size - sizeof(SubsectionBlock),
section.bktr.subsection.offset + sizeof(SubsectionBlock) -
offset) !=
section.bktr.subsection.size - sizeof(SubsectionBlock)) {
status = Loader::ResultStatus::ErrorBadSubsectionBuckets;
return;
}
std::vector<RelocationBucket> relocation_buckets(relocation_buckets_raw.size());
std::transform(relocation_buckets_raw.begin(), relocation_buckets_raw.end(),
relocation_buckets.begin(), &ConvertRelocationBucketRaw);
std::vector<SubsectionBucket> subsection_buckets(subsection_buckets_raw.size());
std::transform(subsection_buckets_raw.begin(), subsection_buckets_raw.end(),
subsection_buckets.begin(), &ConvertSubsectionBucketRaw);
u32 ctr_low;
std::memcpy(&ctr_low, section.raw.section_ctr.data(), sizeof(ctr_low));
subsection_buckets.back().entries.push_back(
{section.bktr.relocation.offset, {0}, ctr_low});
subsection_buckets.back().entries.push_back({size, {0}, 0});
boost::optional<Core::Crypto::Key128> key = boost::none;
if (encrypted) {
if (has_rights_id) {
status = Loader::ResultStatus::Success;
key = GetTitlekey();
if (key == boost::none) {
status = Loader::ResultStatus::ErrorMissingTitlekey;
return;
}
} else {
key = GetKeyAreaKey(NCASectionCryptoType::BKTR);
if (key == boost::none) {
status = Loader::ResultStatus::ErrorMissingKeyAreaKey;
return;
}
}
}
if (bktr_base_romfs == nullptr) {
status = Loader::ResultStatus::ErrorMissingBKTRBaseRomFS;
return;
}
auto bktr = std::make_shared<BKTR>(
bktr_base_romfs, std::make_shared<OffsetVfsFile>(file, romfs_size, base_offset),
relocation_block, relocation_buckets, subsection_block, subsection_buckets,
encrypted, encrypted ? key.get() : Core::Crypto::Key128{}, base_offset,
bktr_base_ivfc_offset, section.raw.section_ctr);
// BKTR applies to entire IVFC, so make an offset version to level 6
files.push_back(std::make_shared<OffsetVfsFile>(
bktr, romfs_size, section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset));
romfs = files.back();
} else {
files.push_back(std::move(dec));
romfs = files.back();
}
} else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) {
u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) *
MEDIA_OFFSET_MULTIPLIER) +
@@ -300,6 +441,12 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) {
dirs.push_back(std::move(npfs));
if (IsDirectoryExeFS(dirs.back()))
exefs = dirs.back();
} else {
if (has_rights_id)
status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
else
status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
return;
}
} else {
if (status != Loader::ResultStatus::Success)
@@ -345,11 +492,15 @@ NCAContentType NCA::GetType() const {
}
u64 NCA::GetTitleId() const {
if (status != Loader::ResultStatus::Success)
return {};
if (is_update || status == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS)
return header.title_id | 0x800;
return header.title_id;
}
bool NCA::IsUpdate() const {
return is_update;
}
VirtualFile NCA::GetRomFS() const {
return romfs;
}
@@ -362,8 +513,8 @@ VirtualFile NCA::GetBaseFile() const {
return file;
}
bool NCA::IsUpdate() const {
return is_update;
u64 NCA::GetBaseIVFCOffset() const {
return ivfc_offset;
}
bool NCA::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
+13 -6
View File
@@ -12,10 +12,12 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "control_metadata.h"
#include "core/crypto/key_manager.h"
#include "core/file_sys/partition_filesystem.h"
#include "core/loader/loader.h"
#include "core/file_sys/vfs.h"
namespace Loader {
enum class ResultStatus : u16;
}
namespace FileSys {
@@ -77,7 +79,8 @@ bool IsValidNCA(const NCAHeader& header);
// After construction, use GetStatus to determine if the file is valid and ready to be used.
class NCA : public ReadOnlyVfsDirectory {
public:
explicit NCA(VirtualFile file);
explicit NCA(VirtualFile file, VirtualFile bktr_base_romfs = nullptr,
u64 bktr_base_ivfc_offset = 0);
Loader::ResultStatus GetStatus() const;
std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
@@ -87,13 +90,15 @@ public:
NCAContentType GetType() const;
u64 GetTitleId() const;
bool IsUpdate() const;
VirtualFile GetRomFS() const;
VirtualDir GetExeFS() const;
VirtualFile GetBaseFile() const;
bool IsUpdate() const;
// Returns the base ivfc offset used in BKTR patching.
u64 GetBaseIVFCOffset() const;
protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
@@ -110,14 +115,16 @@ private:
VirtualFile romfs = nullptr;
VirtualDir exefs = nullptr;
VirtualFile file;
VirtualFile bktr_base_romfs;
u64 ivfc_offset;
NCAHeader header{};
bool has_rights_id{};
bool is_update{};
Loader::ResultStatus status{};
bool encrypted;
bool is_update;
Core::Crypto::KeyManager keys;
};
+11 -1
View File
@@ -21,7 +21,17 @@ NACP::NACP(VirtualFile file) : raw(std::make_unique<RawNACP>()) {
}
const LanguageEntry& NACP::GetLanguageEntry(Language language) const {
return raw->language_entries.at(static_cast<u8>(language));
if (language != Language::Default) {
return raw->language_entries.at(static_cast<u8>(language));
} else {
for (const auto& language_entry : raw->language_entries) {
if (!language_entry.GetApplicationName().empty())
return language_entry;
}
// Fallback to English
return GetLanguageEntry(Language::AmericanEnglish);
}
}
std::string NACP::GetApplicationName(Language language) const {
+7 -3
View File
@@ -8,6 +8,8 @@
#include <memory>
#include <string>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/file_sys/vfs.h"
namespace FileSys {
@@ -60,6 +62,8 @@ enum class Language : u8 {
Korean = 12,
Taiwanese = 13,
Chinese = 14,
Default = 255,
};
static constexpr std::array<const char*, 15> LANGUAGE_NAMES = {
@@ -74,9 +78,9 @@ static constexpr std::array<const char*, 15> LANGUAGE_NAMES = {
class NACP {
public:
explicit NACP(VirtualFile file);
const LanguageEntry& GetLanguageEntry(Language language = Language::AmericanEnglish) const;
std::string GetApplicationName(Language language = Language::AmericanEnglish) const;
std::string GetDeveloperName(Language language = Language::AmericanEnglish) const;
const LanguageEntry& GetLanguageEntry(Language language = Language::Default) const;
std::string GetApplicationName(Language language = Language::Default) const;
std::string GetDeveloperName(Language language = Language::Default) const;
u64 GetTitleId() const;
std::string GetVersionString() const;
+1 -2
View File
@@ -3,10 +3,9 @@
// Refer to the license.txt file included.
#include <cstring>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "common/swap.h"
#include "content_archive.h"
#include "core/file_sys/nca_metadata.h"
namespace FileSys {
-1
View File
@@ -4,7 +4,6 @@
#pragma once
#include <cstring>
#include <memory>
#include <vector>
#include "common/common_funcs.h"
+206
View File
@@ -0,0 +1,206 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/assert.h"
#include "core/crypto/aes_util.h"
#include "core/file_sys/nca_patch.h"
namespace FileSys {
BKTR::BKTR(VirtualFile base_romfs_, VirtualFile bktr_romfs_, RelocationBlock relocation_,
std::vector<RelocationBucket> relocation_buckets_, SubsectionBlock subsection_,
std::vector<SubsectionBucket> subsection_buckets_, bool is_encrypted_,
Core::Crypto::Key128 key_, u64 base_offset_, u64 ivfc_offset_,
std::array<u8, 8> section_ctr_)
: base_romfs(std::move(base_romfs_)), bktr_romfs(std::move(bktr_romfs_)),
relocation(relocation_), relocation_buckets(std::move(relocation_buckets_)),
subsection(subsection_), subsection_buckets(std::move(subsection_buckets_)),
encrypted(is_encrypted_), key(key_), base_offset(base_offset_), ivfc_offset(ivfc_offset_),
section_ctr(section_ctr_) {
for (size_t i = 0; i < relocation.number_buckets - 1; ++i) {
relocation_buckets[i].entries.push_back({relocation.base_offsets[i + 1], 0, 0});
}
for (size_t i = 0; i < subsection.number_buckets - 1; ++i) {
subsection_buckets[i].entries.push_back({subsection_buckets[i + 1].entries[0].address_patch,
{0},
subsection_buckets[i + 1].entries[0].ctr});
}
relocation_buckets.back().entries.push_back({relocation.size, 0, 0});
}
BKTR::~BKTR() = default;
size_t BKTR::Read(u8* data, size_t length, size_t offset) const {
// Read out of bounds.
if (offset >= relocation.size)
return 0;
const auto relocation = GetRelocationEntry(offset);
const auto section_offset = offset - relocation.address_patch + relocation.address_source;
const auto bktr_read = relocation.from_patch;
const auto next_relocation = GetNextRelocationEntry(offset);
if (offset + length > next_relocation.address_patch) {
const u64 partition = next_relocation.address_patch - offset;
return Read(data, partition, offset) +
Read(data + partition, length - partition, offset + partition);
}
if (!bktr_read) {
ASSERT_MSG(section_offset >= ivfc_offset, "Offset calculation negative.");
return base_romfs->Read(data, length, section_offset - ivfc_offset);
}
if (!encrypted) {
return bktr_romfs->Read(data, length, section_offset);
}
const auto subsection = GetSubsectionEntry(section_offset);
Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(key, Core::Crypto::Mode::CTR);
// Calculate AES IV
std::vector<u8> iv(16);
auto subsection_ctr = subsection.ctr;
auto offset_iv = section_offset + base_offset;
for (size_t i = 0; i < section_ctr.size(); ++i)
iv[i] = section_ctr[0x8 - i - 1];
offset_iv >>= 4;
for (size_t i = 0; i < sizeof(u64); ++i) {
iv[0xF - i] = static_cast<u8>(offset_iv & 0xFF);
offset_iv >>= 8;
}
for (size_t i = 0; i < sizeof(u32); ++i) {
iv[0x7 - i] = static_cast<u8>(subsection_ctr & 0xFF);
subsection_ctr >>= 8;
}
cipher.SetIV(iv);
const auto next_subsection = GetNextSubsectionEntry(section_offset);
if (section_offset + length > next_subsection.address_patch) {
const u64 partition = next_subsection.address_patch - section_offset;
return Read(data, partition, offset) +
Read(data + partition, length - partition, offset + partition);
}
const auto block_offset = section_offset & 0xF;
if (block_offset != 0) {
auto block = bktr_romfs->ReadBytes(0x10, section_offset & ~0xF);
cipher.Transcode(block.data(), block.size(), block.data(), Core::Crypto::Op::Decrypt);
if (length + block_offset < 0x10) {
std::memcpy(data, block.data() + block_offset, std::min(length, block.size()));
return std::min(length, block.size());
}
const auto read = 0x10 - block_offset;
std::memcpy(data, block.data() + block_offset, read);
return read + Read(data + read, length - read, offset + read);
}
const auto raw_read = bktr_romfs->Read(data, length, section_offset);
cipher.Transcode(data, raw_read, data, Core::Crypto::Op::Decrypt);
return raw_read;
}
template <bool Subsection, typename BlockType, typename BucketType>
std::pair<size_t, size_t> BKTR::SearchBucketEntry(u64 offset, BlockType block,
BucketType buckets) const {
if constexpr (Subsection) {
const auto last_bucket = buckets[block.number_buckets - 1];
if (offset >= last_bucket.entries[last_bucket.number_entries].address_patch)
return {block.number_buckets - 1, last_bucket.number_entries};
} else {
ASSERT_MSG(offset <= block.size, "Offset is out of bounds in BKTR relocation block.");
}
size_t bucket_id = std::count_if(block.base_offsets.begin() + 1,
block.base_offsets.begin() + block.number_buckets,
[&offset](u64 base_offset) { return base_offset <= offset; });
const auto bucket = buckets[bucket_id];
if (bucket.number_entries == 1)
return {bucket_id, 0};
size_t low = 0;
size_t mid = 0;
size_t high = bucket.number_entries - 1;
while (low <= high) {
mid = (low + high) / 2;
if (bucket.entries[mid].address_patch > offset) {
high = mid - 1;
} else {
if (mid == bucket.number_entries - 1 ||
bucket.entries[mid + 1].address_patch > offset) {
return {bucket_id, mid};
}
low = mid + 1;
}
}
UNREACHABLE_MSG("Offset could not be found in BKTR block.");
}
RelocationEntry BKTR::GetRelocationEntry(u64 offset) const {
const auto res = SearchBucketEntry<false>(offset, relocation, relocation_buckets);
return relocation_buckets[res.first].entries[res.second];
}
RelocationEntry BKTR::GetNextRelocationEntry(u64 offset) const {
const auto res = SearchBucketEntry<false>(offset, relocation, relocation_buckets);
const auto bucket = relocation_buckets[res.first];
if (res.second + 1 < bucket.entries.size())
return bucket.entries[res.second + 1];
return relocation_buckets[res.first + 1].entries[0];
}
SubsectionEntry BKTR::GetSubsectionEntry(u64 offset) const {
const auto res = SearchBucketEntry<true>(offset, subsection, subsection_buckets);
return subsection_buckets[res.first].entries[res.second];
}
SubsectionEntry BKTR::GetNextSubsectionEntry(u64 offset) const {
const auto res = SearchBucketEntry<true>(offset, subsection, subsection_buckets);
const auto bucket = subsection_buckets[res.first];
if (res.second + 1 < bucket.entries.size())
return bucket.entries[res.second + 1];
return subsection_buckets[res.first + 1].entries[0];
}
std::string BKTR::GetName() const {
return base_romfs->GetName();
}
size_t BKTR::GetSize() const {
return relocation.size;
}
bool BKTR::Resize(size_t new_size) {
return false;
}
std::shared_ptr<VfsDirectory> BKTR::GetContainingDirectory() const {
return base_romfs->GetContainingDirectory();
}
bool BKTR::IsWritable() const {
return false;
}
bool BKTR::IsReadable() const {
return true;
}
size_t BKTR::Write(const u8* data, size_t length, size_t offset) {
return 0;
}
bool BKTR::Rename(std::string_view name) {
return base_romfs->Rename(name);
}
} // namespace FileSys
+147
View File
@@ -0,0 +1,147 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <vector>
#include <common/common_funcs.h>
#include "core/crypto/key_manager.h"
#include "core/file_sys/romfs.h"
namespace FileSys {
#pragma pack(push, 1)
struct RelocationEntry {
u64_le address_patch;
u64_le address_source;
u32 from_patch;
};
#pragma pack(pop)
static_assert(sizeof(RelocationEntry) == 0x14, "RelocationEntry has incorrect size.");
struct RelocationBucketRaw {
INSERT_PADDING_BYTES(4);
u32_le number_entries;
u64_le end_offset;
std::array<RelocationEntry, 0x332> relocation_entries;
INSERT_PADDING_BYTES(8);
};
static_assert(sizeof(RelocationBucketRaw) == 0x4000, "RelocationBucketRaw has incorrect size.");
// Vector version of RelocationBucketRaw
struct RelocationBucket {
u32 number_entries;
u64 end_offset;
std::vector<RelocationEntry> entries;
};
struct RelocationBlock {
INSERT_PADDING_BYTES(4);
u32_le number_buckets;
u64_le size;
std::array<u64, 0x7FE> base_offsets;
};
static_assert(sizeof(RelocationBlock) == 0x4000, "RelocationBlock has incorrect size.");
struct SubsectionEntry {
u64_le address_patch;
INSERT_PADDING_BYTES(0x4);
u32_le ctr;
};
static_assert(sizeof(SubsectionEntry) == 0x10, "SubsectionEntry has incorrect size.");
struct SubsectionBucketRaw {
INSERT_PADDING_BYTES(4);
u32_le number_entries;
u64_le end_offset;
std::array<SubsectionEntry, 0x3FF> subsection_entries;
};
static_assert(sizeof(SubsectionBucketRaw) == 0x4000, "SubsectionBucketRaw has incorrect size.");
// Vector version of SubsectionBucketRaw
struct SubsectionBucket {
u32 number_entries;
u64 end_offset;
std::vector<SubsectionEntry> entries;
};
struct SubsectionBlock {
INSERT_PADDING_BYTES(4);
u32_le number_buckets;
u64_le size;
std::array<u64, 0x7FE> base_offsets;
};
static_assert(sizeof(SubsectionBlock) == 0x4000, "SubsectionBlock has incorrect size.");
inline RelocationBucket ConvertRelocationBucketRaw(RelocationBucketRaw raw) {
return {raw.number_entries,
raw.end_offset,
{raw.relocation_entries.begin(), raw.relocation_entries.begin() + raw.number_entries}};
}
inline SubsectionBucket ConvertSubsectionBucketRaw(SubsectionBucketRaw raw) {
return {raw.number_entries,
raw.end_offset,
{raw.subsection_entries.begin(), raw.subsection_entries.begin() + raw.number_entries}};
}
class BKTR : public VfsFile {
public:
BKTR(VirtualFile base_romfs, VirtualFile bktr_romfs, RelocationBlock relocation,
std::vector<RelocationBucket> relocation_buckets, SubsectionBlock subsection,
std::vector<SubsectionBucket> subsection_buckets, bool is_encrypted,
Core::Crypto::Key128 key, u64 base_offset, u64 ivfc_offset, std::array<u8, 8> section_ctr);
~BKTR() override;
size_t Read(u8* data, size_t length, size_t offset) const override;
std::string GetName() const override;
size_t GetSize() const override;
bool Resize(size_t new_size) override;
std::shared_ptr<VfsDirectory> GetContainingDirectory() const override;
bool IsWritable() const override;
bool IsReadable() const override;
size_t Write(const u8* data, size_t length, size_t offset) override;
bool Rename(std::string_view name) override;
private:
template <bool Subsection, typename BlockType, typename BucketType>
std::pair<size_t, size_t> SearchBucketEntry(u64 offset, BlockType block,
BucketType buckets) const;
RelocationEntry GetRelocationEntry(u64 offset) const;
RelocationEntry GetNextRelocationEntry(u64 offset) const;
SubsectionEntry GetSubsectionEntry(u64 offset) const;
SubsectionEntry GetNextSubsectionEntry(u64 offset) const;
RelocationBlock relocation;
std::vector<RelocationBucket> relocation_buckets;
SubsectionBlock subsection;
std::vector<SubsectionBucket> subsection_buckets;
// Should be the raw base romfs, decrypted.
VirtualFile base_romfs;
// Should be the raw BKTR romfs, (located at media_offset with size media_size).
VirtualFile bktr_romfs;
bool encrypted;
Core::Crypto::Key128 key;
// Base offset into NCA, used for IV calculation.
u64 base_offset;
// Distance between IVFC start and RomFS start, used for base reads
u64 ivfc_offset;
std::array<u8, 8> section_ctr;
};
} // namespace FileSys
+153
View File
@@ -0,0 +1,153 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/file_sys/content_archive.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h"
namespace FileSys {
constexpr u64 SINGLE_BYTE_MODULUS = 0x100;
std::string FormatTitleVersion(u32 version, TitleVersionFormat format) {
std::array<u8, sizeof(u32)> bytes{};
bytes[0] = version % SINGLE_BYTE_MODULUS;
for (size_t i = 1; i < bytes.size(); ++i) {
version /= SINGLE_BYTE_MODULUS;
bytes[i] = version % SINGLE_BYTE_MODULUS;
}
if (format == TitleVersionFormat::FourElements)
return fmt::format("v{}.{}.{}.{}", bytes[3], bytes[2], bytes[1], bytes[0]);
return fmt::format("v{}.{}.{}", bytes[3], bytes[2], bytes[1]);
}
constexpr std::array<const char*, 1> PATCH_TYPE_NAMES{
"Update",
};
std::string FormatPatchTypeName(PatchType type) {
return PATCH_TYPE_NAMES.at(static_cast<size_t>(type));
}
PatchManager::PatchManager(u64 title_id) : title_id(title_id) {}
VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id);
if (exefs == nullptr)
return exefs;
const auto installed = Service::FileSystem::GetUnionContents();
// Game Updates
const auto update_tid = GetUpdateTitleID(title_id);
const auto update = installed->GetEntry(update_tid, ContentRecordType::Program);
if (update != nullptr) {
if (update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS &&
update->GetExeFS() != nullptr) {
LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully",
FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0)));
exefs = update->GetExeFS();
}
}
return exefs;
}
VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset,
ContentRecordType type) const {
LOG_INFO(Loader, "Patching RomFS for title_id={:016X}, type={:02X}", title_id,
static_cast<u8>(type));
if (romfs == nullptr)
return romfs;
const auto installed = Service::FileSystem::GetUnionContents();
// Game Updates
const auto update_tid = GetUpdateTitleID(title_id);
const auto update = installed->GetEntryRaw(update_tid, type);
if (update != nullptr) {
const auto new_nca = std::make_shared<NCA>(update, romfs, ivfc_offset);
if (new_nca->GetStatus() == Loader::ResultStatus::Success &&
new_nca->GetRomFS() != nullptr) {
LOG_INFO(Loader, " RomFS: Update ({}) applied successfully",
FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0)));
romfs = new_nca->GetRomFS();
}
}
return romfs;
}
std::map<PatchType, std::string> PatchManager::GetPatchVersionNames() const {
std::map<PatchType, std::string> out;
const auto installed = Service::FileSystem::GetUnionContents();
const auto update_tid = GetUpdateTitleID(title_id);
PatchManager update{update_tid};
auto [nacp, discard_icon_file] = update.GetControlMetadata();
if (nacp != nullptr) {
out[PatchType::Update] = nacp->GetVersionString();
} else {
if (installed->HasEntry(update_tid, ContentRecordType::Program)) {
const auto meta_ver = installed->GetEntryVersion(update_tid);
if (meta_ver == boost::none || meta_ver.get() == 0) {
out[PatchType::Update] = "";
} else {
out[PatchType::Update] =
FormatTitleVersion(meta_ver.get(), TitleVersionFormat::ThreeElements);
}
}
}
return out;
}
std::pair<std::shared_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const {
const auto& installed{Service::FileSystem::GetUnionContents()};
const auto base_control_nca = installed->GetEntry(title_id, ContentRecordType::Control);
if (base_control_nca == nullptr)
return {};
return ParseControlNCA(base_control_nca);
}
std::pair<std::shared_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA(
const std::shared_ptr<NCA>& nca) const {
const auto base_romfs = nca->GetRomFS();
if (base_romfs == nullptr)
return {};
const auto romfs = PatchRomFS(base_romfs, nca->GetBaseIVFCOffset(), ContentRecordType::Control);
if (romfs == nullptr)
return {};
const auto extracted = ExtractRomFS(romfs);
if (extracted == nullptr)
return {};
auto nacp_file = extracted->GetFile("control.nacp");
if (nacp_file == nullptr)
nacp_file = extracted->GetFile("Control.nacp");
const auto nacp = nacp_file == nullptr ? nullptr : std::make_shared<NACP>(nacp_file);
VirtualFile icon_file;
for (const auto& language : FileSys::LANGUAGE_NAMES) {
icon_file = extracted->GetFile("icon_" + std::string(language) + ".dat");
if (icon_file != nullptr)
break;
}
return {nacp, icon_file};
}
} // namespace FileSys
+62
View File
@@ -0,0 +1,62 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <map>
#include <string>
#include "common/common_types.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/vfs.h"
namespace FileSys {
class NCA;
class NACP;
enum class TitleVersionFormat : u8 {
ThreeElements, ///< vX.Y.Z
FourElements, ///< vX.Y.Z.W
};
std::string FormatTitleVersion(u32 version,
TitleVersionFormat format = TitleVersionFormat::ThreeElements);
enum class PatchType {
Update,
};
std::string FormatPatchTypeName(PatchType type);
// A centralized class to manage patches to games.
class PatchManager {
public:
explicit PatchManager(u64 title_id);
// Currently tracked ExeFS patches:
// - Game Updates
VirtualDir PatchExeFS(VirtualDir exefs) const;
// Currently tracked RomFS patches:
// - Game Updates
VirtualFile PatchRomFS(VirtualFile base, u64 ivfc_offset,
ContentRecordType type = ContentRecordType::Program) const;
// Returns a vector of pairs between patch names and patch versions.
// i.e. Update v80 will return {Update, 80}
std::map<PatchType, std::string> GetPatchVersionNames() const;
// Given title_id of the program, attempts to get the control data of the update and parse it,
// falling back to the base control data.
std::pair<std::shared_ptr<NACP>, VirtualFile> GetControlMetadata() const;
// Version of GetControlMetadata that takes an arbitrary NCA
std::pair<std::shared_ptr<NACP>, VirtualFile> ParseControlNCA(
const std::shared_ptr<NCA>& nca) const;
private:
u64 title_id;
};
} // namespace FileSys
+4 -1
View File
@@ -2,7 +2,10 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/file_util.h"
#include <cstddef>
#include <cstring>
#include <vector>
#include "common/logging/log.h"
#include "core/file_sys/program_metadata.h"
#include "core/loader/loader.h"
+1 -3
View File
@@ -5,12 +5,10 @@
#pragma once
#include <array>
#include <string>
#include <vector>
#include "common/bit_field.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "partition_filesystem.h"
#include "core/file_sys/vfs.h"
namespace Loader {
enum class ResultStatus : u16;
+132 -9
View File
@@ -5,13 +5,17 @@
#include <regex>
#include <mbedtls/sha256.h>
#include "common/assert.h"
#include "common/file_util.h"
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "core/crypto/encryption_layer.h"
#include "core/crypto/key_manager.h"
#include "core/file_sys/card_image.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/submission_package.h"
#include "core/file_sys/vfs_concat.h"
#include "core/loader/loader.h"
namespace FileSys {
std::string RegisteredCacheEntry::DebugInfo() const {
@@ -276,6 +280,18 @@ VirtualFile RegisteredCache::GetEntryUnparsed(RegisteredCacheEntry entry) const
return GetEntryUnparsed(entry.title_id, entry.type);
}
boost::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const {
const auto meta_iter = meta.find(title_id);
if (meta_iter != meta.end())
return meta_iter->second.GetTitleVersion();
const auto yuzu_meta_iter = yuzu_meta.find(title_id);
if (yuzu_meta_iter != yuzu_meta.end())
return yuzu_meta_iter->second.GetTitleVersion();
return boost::none;
}
VirtualFile RegisteredCache::GetEntryRaw(u64 title_id, ContentRecordType type) const {
const auto id = GetNcaIDFromMetadata(title_id, type);
if (id == boost::none)
@@ -355,17 +371,21 @@ std::vector<RegisteredCacheEntry> RegisteredCache::ListEntriesFilter(
return out;
}
static std::shared_ptr<NCA> GetNCAFromXCIForID(std::shared_ptr<XCI> xci, const NcaID& id) {
const auto filename = fmt::format("{}.nca", Common::HexArrayToString(id, false));
const auto iter =
std::find_if(xci->GetNCAs().begin(), xci->GetNCAs().end(),
[&filename](std::shared_ptr<NCA> nca) { return nca->GetName() == filename; });
return iter == xci->GetNCAs().end() ? nullptr : *iter;
static std::shared_ptr<NCA> GetNCAFromNSPForID(std::shared_ptr<NSP> nsp, const NcaID& id) {
const auto file = nsp->GetFile(fmt::format("{}.nca", Common::HexArrayToString(id, false)));
if (file == nullptr)
return nullptr;
return std::make_shared<NCA>(file);
}
InstallResult RegisteredCache::InstallEntry(std::shared_ptr<XCI> xci, bool overwrite_if_exists,
const VfsCopyFunction& copy) {
const auto& ncas = xci->GetNCAs();
return InstallEntry(xci->GetSecurePartitionNSP(), overwrite_if_exists, copy);
}
InstallResult RegisteredCache::InstallEntry(std::shared_ptr<NSP> nsp, bool overwrite_if_exists,
const VfsCopyFunction& copy) {
const auto& ncas = nsp->GetNCAsCollapsed();
const auto& meta_iter = std::find_if(ncas.begin(), ncas.end(), [](std::shared_ptr<NCA> nca) {
return nca->GetType() == NCAContentType::Meta;
});
@@ -389,7 +409,7 @@ InstallResult RegisteredCache::InstallEntry(std::shared_ptr<XCI> xci, bool overw
const auto cnmt_file = section0->GetFiles()[0];
const CNMT cnmt(cnmt_file);
for (const auto& record : cnmt.GetContentRecords()) {
const auto nca = GetNCAFromXCIForID(xci, record.nca_id);
const auto nca = GetNCAFromNSPForID(nsp, record.nca_id);
if (nca == nullptr)
return InstallResult::ErrorCopyFailed;
const auto res2 = RawInstallNCA(nca, copy, overwrite_if_exists, record.nca_id);
@@ -490,4 +510,107 @@ bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) {
kv.second.GetTitleID() == cnmt.GetTitleID();
}) != yuzu_meta.end();
}
RegisteredCacheUnion::RegisteredCacheUnion(std::vector<std::shared_ptr<RegisteredCache>> caches)
: caches(std::move(caches)) {}
void RegisteredCacheUnion::Refresh() {
for (const auto& c : caches)
c->Refresh();
}
bool RegisteredCacheUnion::HasEntry(u64 title_id, ContentRecordType type) const {
return std::any_of(caches.begin(), caches.end(), [title_id, type](const auto& cache) {
return cache->HasEntry(title_id, type);
});
}
bool RegisteredCacheUnion::HasEntry(RegisteredCacheEntry entry) const {
return HasEntry(entry.title_id, entry.type);
}
boost::optional<u32> RegisteredCacheUnion::GetEntryVersion(u64 title_id) const {
for (const auto& c : caches) {
const auto res = c->GetEntryVersion(title_id);
if (res != boost::none)
return res;
}
return boost::none;
}
VirtualFile RegisteredCacheUnion::GetEntryUnparsed(u64 title_id, ContentRecordType type) const {
for (const auto& c : caches) {
const auto res = c->GetEntryUnparsed(title_id, type);
if (res != nullptr)
return res;
}
return nullptr;
}
VirtualFile RegisteredCacheUnion::GetEntryUnparsed(RegisteredCacheEntry entry) const {
return GetEntryUnparsed(entry.title_id, entry.type);
}
VirtualFile RegisteredCacheUnion::GetEntryRaw(u64 title_id, ContentRecordType type) const {
for (const auto& c : caches) {
const auto res = c->GetEntryRaw(title_id, type);
if (res != nullptr)
return res;
}
return nullptr;
}
VirtualFile RegisteredCacheUnion::GetEntryRaw(RegisteredCacheEntry entry) const {
return GetEntryRaw(entry.title_id, entry.type);
}
std::shared_ptr<NCA> RegisteredCacheUnion::GetEntry(u64 title_id, ContentRecordType type) const {
const auto raw = GetEntryRaw(title_id, type);
if (raw == nullptr)
return nullptr;
return std::make_shared<NCA>(raw);
}
std::shared_ptr<NCA> RegisteredCacheUnion::GetEntry(RegisteredCacheEntry entry) const {
return GetEntry(entry.title_id, entry.type);
}
std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntries() const {
std::vector<RegisteredCacheEntry> out;
for (const auto& c : caches) {
c->IterateAllMetadata<RegisteredCacheEntry>(
out,
[](const CNMT& c, const ContentRecord& r) {
return RegisteredCacheEntry{c.GetTitleID(), r.type};
},
[](const CNMT& c, const ContentRecord& r) { return true; });
}
return out;
}
std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntriesFilter(
boost::optional<TitleType> title_type, boost::optional<ContentRecordType> record_type,
boost::optional<u64> title_id) const {
std::vector<RegisteredCacheEntry> out;
for (const auto& c : caches) {
c->IterateAllMetadata<RegisteredCacheEntry>(
out,
[](const CNMT& c, const ContentRecord& r) {
return RegisteredCacheEntry{c.GetTitleID(), r.type};
},
[&title_type, &record_type, &title_id](const CNMT& c, const ContentRecord& r) {
if (title_type != boost::none && title_type.get() != c.GetType())
return false;
if (record_type != boost::none && record_type.get() != r.type)
return false;
if (title_id != boost::none && title_id.get() != c.GetTitleID())
return false;
return true;
});
}
return out;
}
} // namespace FileSys
+52 -6
View File
@@ -11,15 +11,19 @@
#include <string>
#include <vector>
#include <boost/container/flat_map.hpp>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "content_archive.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/vfs.h"
namespace FileSys {
class XCI;
class CNMT;
class NCA;
class NSP;
class XCI;
enum class ContentRecordType : u8;
enum class TitleType : u8;
struct ContentRecord;
using NcaID = std::array<u8, 0x10>;
using RegisteredCacheParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>;
@@ -39,6 +43,10 @@ struct RegisteredCacheEntry {
std::string DebugInfo() const;
};
constexpr u64 GetUpdateTitleID(u64 base_title_id) {
return base_title_id | 0x800;
}
// boost flat_map requires operator< for O(log(n)) lookups.
bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs);
@@ -56,6 +64,8 @@ bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs)
* 4GB splitting can be ignored.)
*/
class RegisteredCache {
friend class RegisteredCacheUnion;
public:
// Parsing function defines the conversion from raw file to NCA. If there are other steps
// besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom
@@ -70,6 +80,8 @@ public:
bool HasEntry(u64 title_id, ContentRecordType type) const;
bool HasEntry(RegisteredCacheEntry entry) const;
boost::optional<u32> GetEntryVersion(u64 title_id) const;
VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const;
VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const;
@@ -86,10 +98,12 @@ public:
boost::optional<ContentRecordType> record_type = boost::none,
boost::optional<u64> title_id = boost::none) const;
// Raw copies all the ncas from the xci to the csache. Does some quick checks to make sure there
// is a meta NCA and all of them are accessible.
// Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure
// there is a meta NCA and all of them are accessible.
InstallResult InstallEntry(std::shared_ptr<XCI> xci, bool overwrite_if_exists = false,
const VfsCopyFunction& copy = &VfsRawCopy);
InstallResult InstallEntry(std::shared_ptr<NSP> nsp, bool overwrite_if_exists = false,
const VfsCopyFunction& copy = &VfsRawCopy);
// Due to the fact that we must use Meta-type NCAs to determine the existance of files, this
// poses quite a challenge. Instead of creating a new meta NCA for this file, yuzu will create a
@@ -125,4 +139,36 @@ private:
boost::container::flat_map<u64, CNMT> yuzu_meta;
};
// Combines multiple RegisteredCaches (i.e. SysNAND, UserNAND, SDMC) into one interface.
class RegisteredCacheUnion {
public:
explicit RegisteredCacheUnion(std::vector<std::shared_ptr<RegisteredCache>> caches);
void Refresh();
bool HasEntry(u64 title_id, ContentRecordType type) const;
bool HasEntry(RegisteredCacheEntry entry) const;
boost::optional<u32> GetEntryVersion(u64 title_id) const;
VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const;
VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const;
VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const;
VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const;
std::shared_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const;
std::shared_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
std::vector<RegisteredCacheEntry> ListEntries() const;
// If a parameter is not boost::none, it will be filtered for from all entries.
std::vector<RegisteredCacheEntry> ListEntriesFilter(
boost::optional<TitleType> title_type = boost::none,
boost::optional<ContentRecordType> record_type = boost::none,
boost::optional<u64> title_id = boost::none) const;
private:
std::vector<std::shared_ptr<RegisteredCache>> caches;
};
} // namespace FileSys
+1
View File
@@ -6,6 +6,7 @@
#include <array>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/file_sys/vfs.h"
+12 -2
View File
@@ -2,11 +2,14 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <memory>
#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs_factory.h"
#include "core/hle/kernel/process.h"
@@ -20,10 +23,17 @@ RomFSFactory::RomFSFactory(Loader::AppLoader& app_loader) {
if (app_loader.ReadRomFS(file) != Loader::ResultStatus::Success) {
LOG_ERROR(Service_FS, "Unable to read RomFS!");
}
updatable = app_loader.IsRomFSUpdatable();
ivfc_offset = app_loader.ReadRomFSIVFCOffset();
}
ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() {
return MakeResult<VirtualFile>(file);
if (!updatable)
return MakeResult<VirtualFile>(file);
const PatchManager patch_manager(Core::CurrentProcess()->program_id);
return MakeResult<VirtualFile>(patch_manager.PatchRomFS(file, ivfc_offset));
}
ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, ContentRecordType type) {
+2
View File
@@ -36,6 +36,8 @@ public:
private:
VirtualFile file;
bool updatable;
u64 ivfc_offset;
};
} // namespace FileSys
+2
View File
@@ -3,10 +3,12 @@
// Refer to the license.txt file included.
#include <memory>
#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/vfs.h"
#include "core/hle/kernel/process.h"
namespace FileSys {
+2
View File
@@ -6,8 +6,10 @@
#include <memory>
#include <string>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/file_sys/vfs.h"
#include "core/hle/result.h"
namespace FileSys {
+245
View File
@@ -0,0 +1,245 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <cstring>
#include <string_view>
#include <fmt/ostream.h>
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "core/crypto/key_manager.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/partition_filesystem.h"
#include "core/file_sys/submission_package.h"
#include "core/loader/loader.h"
namespace FileSys {
NSP::NSP(VirtualFile file_)
: file(std::move(file_)), status{Loader::ResultStatus::Success},
pfs(std::make_shared<PartitionFilesystem>(file)) {
if (pfs->GetStatus() != Loader::ResultStatus::Success) {
status = pfs->GetStatus();
return;
}
if (IsDirectoryExeFS(pfs)) {
extracted = true;
exefs = pfs;
const auto& files = pfs->GetFiles();
const auto romfs_iter =
std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& file) {
return file->GetName().find(".romfs") != std::string::npos;
});
if (romfs_iter != files.end())
romfs = *romfs_iter;
return;
}
extracted = false;
const auto files = pfs->GetFiles();
Core::Crypto::KeyManager keys;
for (const auto& ticket_file : files) {
if (ticket_file->GetExtension() == "tik") {
if (ticket_file == nullptr ||
ticket_file->GetSize() <
Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET + sizeof(Core::Crypto::Key128)) {
continue;
}
Core::Crypto::Key128 key{};
ticket_file->Read(key.data(), key.size(), Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET);
std::string_view name_only(ticket_file->GetName());
name_only.remove_suffix(4);
const auto rights_id_raw = Common::HexStringToArray<16>(name_only);
u128 rights_id;
std::memcpy(rights_id.data(), rights_id_raw.data(), sizeof(u128));
keys.SetKey(Core::Crypto::S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
}
}
for (const auto& outer_file : files) {
if (outer_file->GetName().substr(outer_file->GetName().size() - 9) == ".cnmt.nca") {
const auto nca = std::make_shared<NCA>(outer_file);
if (nca->GetStatus() != Loader::ResultStatus::Success) {
program_status[nca->GetTitleId()] = nca->GetStatus();
continue;
}
const auto section0 = nca->GetSubdirectories()[0];
for (const auto& inner_file : section0->GetFiles()) {
if (inner_file->GetExtension() != "cnmt")
continue;
const CNMT cnmt(inner_file);
auto& ncas_title = ncas[cnmt.GetTitleID()];
ncas_title[ContentRecordType::Meta] = nca;
for (const auto& rec : cnmt.GetContentRecords()) {
const auto id_string = Common::HexArrayToString(rec.nca_id, false);
const auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string));
if (next_file == nullptr) {
LOG_WARNING(Service_FS,
"NCA with ID {}.nca is listed in content metadata, but cannot "
"be found in PFS. NSP appears to be corrupted.",
id_string);
continue;
}
auto next_nca = std::make_shared<NCA>(next_file);
if (next_nca->GetType() == NCAContentType::Program)
program_status[cnmt.GetTitleID()] = next_nca->GetStatus();
if (next_nca->GetStatus() == Loader::ResultStatus::Success)
ncas_title[rec.type] = std::move(next_nca);
}
break;
}
}
}
}
NSP::~NSP() = default;
Loader::ResultStatus NSP::GetStatus() const {
return status;
}
Loader::ResultStatus NSP::GetProgramStatus(u64 title_id) const {
const auto iter = program_status.find(title_id);
if (iter == program_status.end())
return Loader::ResultStatus::ErrorNSPMissingProgramNCA;
return iter->second;
}
u64 NSP::GetFirstTitleID() const {
if (program_status.empty())
return 0;
return program_status.begin()->first;
}
u64 NSP::GetProgramTitleID() const {
const auto out = GetFirstTitleID();
if ((out & 0x800) == 0)
return out;
const auto ids = GetTitleIDs();
const auto iter =
std::find_if(ids.begin(), ids.end(), [](u64 tid) { return (tid & 0x800) == 0; });
return iter == ids.end() ? out : *iter;
}
std::vector<u64> NSP::GetTitleIDs() const {
std::vector<u64> out;
out.reserve(ncas.size());
for (const auto& kv : ncas)
out.push_back(kv.first);
return out;
}
bool NSP::IsExtractedType() const {
return extracted;
}
VirtualFile NSP::GetRomFS() const {
return romfs;
}
VirtualDir NSP::GetExeFS() const {
return exefs;
}
std::vector<std::shared_ptr<NCA>> NSP::GetNCAsCollapsed() const {
if (extracted)
LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
std::vector<std::shared_ptr<NCA>> out;
for (const auto& map : ncas) {
for (const auto& inner_map : map.second)
out.push_back(inner_map.second);
}
return out;
}
std::multimap<u64, std::shared_ptr<NCA>> NSP::GetNCAsByTitleID() const {
if (extracted)
LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
std::multimap<u64, std::shared_ptr<NCA>> out;
for (const auto& map : ncas) {
for (const auto& inner_map : map.second)
out.emplace(map.first, inner_map.second);
}
return out;
}
std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> NSP::GetNCAs() const {
return ncas;
}
std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type) const {
if (extracted)
LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
const auto title_id_iter = ncas.find(title_id);
if (title_id_iter == ncas.end())
return nullptr;
const auto type_iter = title_id_iter->second.find(type);
if (type_iter == title_id_iter->second.end())
return nullptr;
return type_iter->second;
}
VirtualFile NSP::GetNCAFile(u64 title_id, ContentRecordType type) const {
if (extracted)
LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
const auto nca = GetNCA(title_id, type);
if (nca != nullptr)
return nca->GetBaseFile();
return nullptr;
}
std::vector<Core::Crypto::Key128> NSP::GetTitlekey() const {
if (extracted)
LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
std::vector<Core::Crypto::Key128> out;
for (const auto& ticket_file : ticket_files) {
if (ticket_file == nullptr ||
ticket_file->GetSize() <
Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET + sizeof(Core::Crypto::Key128)) {
continue;
}
out.emplace_back();
ticket_file->Read(out.back().data(), out.back().size(),
Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET);
}
return out;
}
std::vector<VirtualFile> NSP::GetFiles() const {
return pfs->GetFiles();
}
std::vector<VirtualDir> NSP::GetSubdirectories() const {
return pfs->GetSubdirectories();
}
std::string NSP::GetName() const {
return file->GetName();
}
VirtualDir NSP::GetParentDirectory() const {
return file->GetContainingDirectory();
}
bool NSP::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
return false;
}
} // namespace FileSys
+76
View File
@@ -0,0 +1,76 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <map>
#include <memory>
#include <vector>
#include "common/common_types.h"
#include "core/file_sys/vfs.h"
namespace Loader {
enum class ResultStatus : u16;
}
namespace FileSys {
class NCA;
class PartitionFilesystem;
enum class ContentRecordType : u8;
class NSP : public ReadOnlyVfsDirectory {
public:
explicit NSP(VirtualFile file);
~NSP();
Loader::ResultStatus GetStatus() const;
Loader::ResultStatus GetProgramStatus(u64 title_id) const;
// Should only be used when one title id can be assured.
u64 GetFirstTitleID() const;
u64 GetProgramTitleID() const;
std::vector<u64> GetTitleIDs() const;
bool IsExtractedType() const;
// Common (Can be safely called on both types)
VirtualFile GetRomFS() const;
VirtualDir GetExeFS() const;
// Type 0 Only (Collection of NCAs + Certificate + Ticket + Meta XML)
std::vector<std::shared_ptr<NCA>> GetNCAsCollapsed() const;
std::multimap<u64, std::shared_ptr<NCA>> GetNCAsByTitleID() const;
std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> GetNCAs() const;
std::shared_ptr<NCA> GetNCA(u64 title_id, ContentRecordType type) const;
VirtualFile GetNCAFile(u64 title_id, ContentRecordType type) const;
std::vector<Core::Crypto::Key128> GetTitlekey() const;
std::vector<VirtualFile> GetFiles() const override;
std::vector<VirtualDir> GetSubdirectories() const override;
std::string GetName() const override;
VirtualDir GetParentDirectory() const override;
protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
private:
VirtualFile file;
bool extracted;
Loader::ResultStatus status;
std::map<u64, Loader::ResultStatus> program_status;
std::shared_ptr<PartitionFilesystem> pfs;
// Map title id -> {map type -> NCA}
std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> ncas;
std::vector<VirtualFile> ticket_files;
VirtualFile romfs;
VirtualDir exefs;
};
} // namespace FileSys
+6
View File
@@ -8,6 +8,7 @@
#include <utility>
#include "common/assert.h"
#include "common/common_paths.h"
#include "common/file_util.h"
#include "common/logging/log.h"
#include "core/file_sys/vfs_real.h"
@@ -39,6 +40,7 @@ static std::string ModeFlagsToString(Mode mode) {
}
RealVfsFilesystem::RealVfsFilesystem() : VfsFilesystem(nullptr) {}
RealVfsFilesystem::~RealVfsFilesystem() = default;
std::string RealVfsFilesystem::GetName() const {
return "Real";
@@ -219,6 +221,8 @@ RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::shared_ptr<FileUtil::IOF
parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)),
perms(perms_) {}
RealVfsFile::~RealVfsFile() = default;
std::string RealVfsFile::GetName() const {
return path_components.back();
}
@@ -312,6 +316,8 @@ RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string&
FileUtil::CreateDir(path);
}
RealVfsDirectory::~RealVfsDirectory() = default;
std::shared_ptr<VfsFile> RealVfsDirectory::GetFileRelative(std::string_view path) const {
const auto full_path = FileUtil::SanitizePath(this->path + DIR_SEP + std::string(path));
if (!FileUtil::Exists(full_path) || FileUtil::IsDirectory(full_path))
+14 -6
View File
@@ -6,15 +6,19 @@
#include <string_view>
#include <boost/container/flat_map.hpp>
#include "common/file_util.h"
#include "core/file_sys/mode.h"
#include "core/file_sys/vfs.h"
namespace FileUtil {
class IOFile;
}
namespace FileSys {
class RealVfsFilesystem : public VfsFilesystem {
public:
RealVfsFilesystem();
~RealVfsFilesystem() override;
std::string GetName() const override;
bool IsReadable() const override;
@@ -40,10 +44,9 @@ class RealVfsFile : public VfsFile {
friend class RealVfsDirectory;
friend class RealVfsFilesystem;
RealVfsFile(RealVfsFilesystem& base, std::shared_ptr<FileUtil::IOFile> backing,
const std::string& path, Mode perms = Mode::Read);
public:
~RealVfsFile() override;
std::string GetName() const override;
size_t GetSize() const override;
bool Resize(size_t new_size) override;
@@ -55,6 +58,9 @@ public:
bool Rename(std::string_view name) override;
private:
RealVfsFile(RealVfsFilesystem& base, std::shared_ptr<FileUtil::IOFile> backing,
const std::string& path, Mode perms = Mode::Read);
bool Close();
RealVfsFilesystem& base;
@@ -70,9 +76,9 @@ private:
class RealVfsDirectory : public VfsDirectory {
friend class RealVfsFilesystem;
RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, Mode perms = Mode::Read);
public:
~RealVfsDirectory() override;
std::shared_ptr<VfsFile> GetFileRelative(std::string_view path) const override;
std::shared_ptr<VfsDirectory> GetDirectoryRelative(std::string_view path) const override;
std::shared_ptr<VfsFile> GetFile(std::string_view name) const override;
@@ -97,6 +103,8 @@ protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
private:
RealVfsDirectory(RealVfsFilesystem& base, const std::string& path, Mode perms = Mode::Read);
template <typename T, typename R>
std::vector<std::shared_ptr<R>> IterateEntries() const;
+1
View File
@@ -10,6 +10,7 @@
#include <mbedtls/md.h>
#include <mbedtls/sha256.h>
#include "common/assert.h"
#include "common/file_util.h"
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "core/crypto/aes_util.h"
+2
View File
@@ -8,9 +8,11 @@
#include "common/assert.h"
#include "common/common_types.h"
#include "core/core.h"
#include "core/core_cpu.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/result.h"
#include "core/memory.h"
+1
View File
@@ -18,6 +18,7 @@
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/server_session.h"
+24
View File
@@ -13,6 +13,7 @@
#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
@@ -124,6 +125,8 @@ struct KernelCore::Impl {
timer_callback_handle_table.Clear();
timer_callback_event_type = nullptr;
named_ports.clear();
}
void InitializeResourceLimits(KernelCore& kernel) {
@@ -217,6 +220,10 @@ struct KernelCore::Impl {
// TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future,
// allowing us to simply use a pool index or similar.
Kernel::HandleTable thread_wakeup_callback_handle_table;
/// Map of named ports managed by the kernel, which can be retrieved using
/// the ConnectToPort SVC.
NamedPortTable named_ports;
};
KernelCore::KernelCore() : impl{std::make_unique<Impl>()} {}
@@ -257,6 +264,23 @@ void KernelCore::AppendNewProcess(SharedPtr<Process> process) {
impl->process_list.push_back(std::move(process));
}
void KernelCore::AddNamedPort(std::string name, SharedPtr<ClientPort> port) {
impl->named_ports.emplace(std::move(name), std::move(port));
}
KernelCore::NamedPortTable::iterator KernelCore::FindNamedPort(const std::string& name) {
return impl->named_ports.find(name);
}
KernelCore::NamedPortTable::const_iterator KernelCore::FindNamedPort(
const std::string& name) const {
return impl->named_ports.find(name);
}
bool KernelCore::IsValidNamedPort(NamedPortTable::const_iterator port) const {
return port != impl->named_ports.cend();
}
u32 KernelCore::CreateNewObjectID() {
return impl->next_object_id++;
}
+18
View File
@@ -4,6 +4,8 @@
#pragma once
#include <string>
#include <unordered_map>
#include "core/hle/kernel/object.h"
template <typename T>
@@ -15,6 +17,7 @@ struct EventType;
namespace Kernel {
class ClientPort;
class HandleTable;
class Process;
class ResourceLimit;
@@ -25,6 +28,9 @@ enum class ResourceLimitCategory : u8;
/// Represents a single instance of the kernel.
class KernelCore {
private:
using NamedPortTable = std::unordered_map<std::string, SharedPtr<ClientPort>>;
public:
KernelCore();
~KernelCore();
@@ -59,6 +65,18 @@ public:
/// Adds the given shared pointer to an internal list of active processes.
void AppendNewProcess(SharedPtr<Process> process);
/// Adds a port to the named port table
void AddNamedPort(std::string name, SharedPtr<ClientPort> port);
/// Finds a port within the named port table with the given name.
NamedPortTable::iterator FindNamedPort(const std::string& name);
/// Finds a port within the named port table with the given name.
NamedPortTable::const_iterator FindNamedPort(const std::string& name) const;
/// Determines whether or not the given port is a valid named port.
bool IsValidNamedPort(NamedPortTable::const_iterator port) const;
private:
friend class Object;
friend class Process;
+3 -3
View File
@@ -13,6 +13,7 @@
#include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/session.h"
@@ -104,11 +105,10 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {
// The ServerSession received a sync request, this means that there's new data available
// from its ClientSession, so wake up any threads that may be waiting on a svcReplyAndReceive or
// similar.
auto& handle_table = Core::System::GetInstance().Kernel().HandleTable();
Kernel::HLERequestContext context(this);
u32* cmd_buf = (u32*)Memory::GetPointer(thread->GetTLSAddress());
context.PopulateFromIncomingCommandBuffer(cmd_buf, *Core::CurrentProcess(), handle_table);
context.PopulateFromIncomingCommandBuffer(cmd_buf, *Core::CurrentProcess(),
kernel.HandleTable());
ResultCode result = RESULT_SUCCESS;
// If the session has been converted to a domain, handle the domain request
+11 -5
View File
@@ -12,16 +12,20 @@
#include "common/logging/log.h"
#include "common/microprofile.h"
#include "common/string_util.h"
#include "core/arm/exclusive_monitor.h"
#include "core/core.h"
#include "core/core_cpu.h"
#include "core/core_timing.h"
#include "core/hle/kernel/address_arbiter.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/event.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/mutex.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/kernel/svc.h"
#include "core/hle/kernel/svc_wrap.h"
@@ -64,19 +68,22 @@ static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
/// Connect to an OS service given the port name, returns the handle to the port to out
static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address) {
if (!Memory::IsValidVirtualAddress(port_name_address))
if (!Memory::IsValidVirtualAddress(port_name_address)) {
return ERR_NOT_FOUND;
}
static constexpr std::size_t PortNameMaxLength = 11;
// Read 1 char beyond the max allowed port name to detect names that are too long.
std::string port_name = Memory::ReadCString(port_name_address, PortNameMaxLength + 1);
if (port_name.size() > PortNameMaxLength)
if (port_name.size() > PortNameMaxLength) {
return ERR_PORT_NAME_TOO_LONG;
}
LOG_TRACE(Kernel_SVC, "called port_name={}", port_name);
auto it = Service::g_kernel_named_ports.find(port_name);
if (it == Service::g_kernel_named_ports.end()) {
auto& kernel = Core::System::GetInstance().Kernel();
auto it = kernel.FindNamedPort(port_name);
if (!kernel.IsValidNamedPort(it)) {
LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: {}", port_name);
return ERR_NOT_FOUND;
}
@@ -87,7 +94,6 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address
CASCADE_RESULT(client_session, client_port->Connect());
// Return the client session
auto& kernel = Core::System::GetInstance().Kernel();
CASCADE_RESULT(*out_handle, kernel.HandleTable().Create(client_session));
return RESULT_SUCCESS;
}
+2 -1
View File
@@ -16,6 +16,7 @@
#include "common/thread_queue_list.h"
#include "core/arm/arm_interface.h"
#include "core/core.h"
#include "core/core_cpu.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
#include "core/hle/kernel/errors.h"
@@ -23,8 +24,8 @@
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/thread.h"
#include "core/hle/lock.h"
#include "core/hle/result.h"
#include "core/memory.h"
+12 -2
View File
@@ -10,6 +10,7 @@
#include "core/file_sys/bis_factory.h"
#include "core/file_sys/errors.h"
#include "core/file_sys/mode.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs_factory.h"
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/sdmc_factory.h"
@@ -60,17 +61,20 @@ ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path_, u64
ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) const {
std::string path(FileUtil::SanitizePath(path_));
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
if (path.empty()) {
// TODO(DarkLordZach): Why do games call this and what should it do? Works as is but...
return RESULT_SUCCESS;
}
if (dir->GetFile(FileUtil::GetFilename(path)) == nullptr)
auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path));
if (dir->GetFile(FileUtil::GetFilename(path)) == nullptr) {
return FileSys::ERROR_PATH_NOT_FOUND;
}
if (!dir->DeleteFile(FileUtil::GetFilename(path))) {
// TODO(DarkLordZach): Find a better error code for this
return ResultCode(-1);
}
return RESULT_SUCCESS;
}
@@ -304,6 +308,12 @@ ResultVal<FileSys::VirtualDir> OpenSDMC() {
return sdmc_factory->Open();
}
std::shared_ptr<FileSys::RegisteredCacheUnion> GetUnionContents() {
return std::make_shared<FileSys::RegisteredCacheUnion>(
std::vector<std::shared_ptr<FileSys::RegisteredCache>>{
GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()});
}
std::shared_ptr<FileSys::RegisteredCache> GetSystemNANDContents() {
LOG_TRACE(Service_FS, "Opening System NAND Contents");
@@ -7,11 +7,13 @@
#include <memory>
#include "common/common_types.h"
#include "core/file_sys/directory.h"
#include "core/file_sys/vfs.h"
#include "core/hle/result.h"
namespace FileSys {
class BISFactory;
class RegisteredCache;
class RegisteredCacheUnion;
class RomFSFactory;
class SaveDataFactory;
class SDMCFactory;
@@ -44,6 +46,8 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
FileSys::SaveDataDescriptor save_struct);
ResultVal<FileSys::VirtualDir> OpenSDMC();
std::shared_ptr<FileSys::RegisteredCacheUnion> GetUnionContents();
std::shared_ptr<FileSys::RegisteredCache> GetSystemNANDContents();
std::shared_ptr<FileSys::RegisteredCache> GetUserNANDContents();
std::shared_ptr<FileSys::RegisteredCache> GetSDMCContents();
+28 -2
View File
@@ -26,6 +26,17 @@
namespace Service::FileSystem {
enum class FileSystemType : u8 {
Invalid0 = 0,
Invalid1 = 1,
Logo = 2,
ContentControl = 3,
ContentManual = 4,
ContentMeta = 5,
ContentData = 6,
ApplicationPackage = 7,
};
class IStorage final : public ServiceFramework<IStorage> {
public:
explicit IStorage(FileSys::VirtualFile backend_)
@@ -420,7 +431,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
{0, nullptr, "MountContent"},
{1, &FSP_SRV::Initialize, "Initialize"},
{2, nullptr, "OpenDataFileSystemByCurrentProcess"},
{7, nullptr, "OpenFileSystemWithPatch"},
{7, &FSP_SRV::OpenFileSystemWithPatch, "OpenFileSystemWithPatch"},
{8, nullptr, "OpenFileSystemWithId"},
{9, nullptr, "OpenDataFileSystemByApplicationId"},
{11, nullptr, "OpenBisFileSystem"},
@@ -444,7 +455,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
{34, nullptr, "GetCacheStorageSize"},
{51, &FSP_SRV::MountSaveData, "MountSaveData"},
{52, nullptr, "OpenSaveDataFileSystemBySystemSaveDataId"},
{53, nullptr, "OpenReadOnlySaveDataFileSystem"},
{53, &FSP_SRV::OpenReadOnlySaveDataFileSystem, "OpenReadOnlySaveDataFileSystem"},
{57, nullptr, "ReadSaveDataFileSystemExtraDataBySaveDataSpaceId"},
{58, nullptr, "ReadSaveDataFileSystemExtraData"},
{59, nullptr, "WriteSaveDataFileSystemExtraData"},
@@ -516,6 +527,16 @@ void FSP_SRV::Initialize(Kernel::HLERequestContext& ctx) {
rb.Push(RESULT_SUCCESS);
}
void FSP_SRV::OpenFileSystemWithPatch(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto type = rp.PopRaw<FileSystemType>();
const auto title_id = rp.PopRaw<u64>();
IPC::ResponseBuilder rb{ctx, 2, 0, 0};
rb.Push(ResultCode(-1));
}
void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_FS, "called");
@@ -563,6 +584,11 @@ void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) {
rb.PushIpcInterface<IFileSystem>(std::move(filesystem));
}
void FSP_SRV::OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called, delegating to 51 OpenSaveDataFilesystem");
MountSaveData(ctx);
}
void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called");
@@ -20,9 +20,11 @@ public:
private:
void Initialize(Kernel::HLERequestContext& ctx);
void OpenFileSystemWithPatch(Kernel::HLERequestContext& ctx);
void MountSdCard(Kernel::HLERequestContext& ctx);
void CreateSaveData(Kernel::HLERequestContext& ctx);
void MountSaveData(Kernel::HLERequestContext& ctx);
void OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx);
void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx);
void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
void OpenDataStorageByDataId(Kernel::HLERequestContext& ctx);
+3 -1
View File
@@ -5,7 +5,9 @@
#include "common/common_paths.h"
#include "common/file_util.h"
#include "core/core.h"
#include "core/file_sys/bis_factory.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/filesystem/filesystem.h"
@@ -10,6 +10,7 @@
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"
#include "video_core/memory_manager.h"
namespace Service::Nvidia::Devices {
+2 -7
View File
@@ -12,6 +12,7 @@
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/server_port.h"
#include "core/hle/kernel/thread.h"
@@ -114,7 +115,7 @@ void ServiceFrameworkBase::InstallAsNamedPort() {
std::tie(server_port, client_port) =
ServerPort::CreatePortPair(kernel, max_sessions, service_name);
server_port->SetHleHandler(shared_from_this());
AddNamedPort(service_name, std::move(client_port));
kernel.AddNamedPort(service_name, std::move(client_port));
}
Kernel::SharedPtr<Kernel::ClientPort> ServiceFrameworkBase::CreatePort() {
@@ -197,11 +198,6 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co
////////////////////////////////////////////////////////////////////////////////////////////////////
// Module interface
// TODO(yuriks): Move to kernel
void AddNamedPort(std::string name, SharedPtr<ClientPort> port) {
g_kernel_named_ports.emplace(std::move(name), std::move(port));
}
/// Initialize ServiceManager
void Init(std::shared_ptr<SM::ServiceManager>& sm, const FileSys::VirtualFilesystem& rfs) {
// NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
@@ -264,7 +260,6 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, const FileSys::VirtualFilesys
/// Shutdown ServiceManager
void Shutdown() {
g_kernel_named_ports.clear();
LOG_DEBUG(Service, "shutdown OK");
}
} // namespace Service
-7
View File
@@ -6,7 +6,6 @@
#include <cstddef>
#include <string>
#include <unordered_map>
#include <boost/container/flat_map.hpp>
#include "common/common_types.h"
#include "core/hle/kernel/hle_ipc.h"
@@ -187,10 +186,4 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm,
/// Shutdown ServiceManager
void Shutdown();
/// Map of named ports managed by the kernel, which can be retrieved using the ConnectToPort SVC.
extern std::unordered_map<std::string, Kernel::SharedPtr<Kernel::ClientPort>> g_kernel_named_ports;
/// Adds a port to the named port table
void AddNamedPort(std::string name, Kernel::SharedPtr<Kernel::ClientPort> port);
} // namespace Service
+36 -26
View File
@@ -3,6 +3,9 @@
// Refer to the license.txt file included.
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
#include "core/hle/service/ssl/ssl.h"
namespace Service::SSL {
@@ -81,36 +84,43 @@ private:
}
};
void SSL::CreateContext(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_SSL, "(STUBBED) called");
class SSL final : public ServiceFramework<SSL> {
public:
explicit SSL() : ServiceFramework{"ssl"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &SSL::CreateContext, "CreateContext"},
{1, nullptr, "GetContextCount"},
{2, nullptr, "GetCertificates"},
{3, nullptr, "GetCertificateBufSize"},
{4, nullptr, "DebugIoctl"},
{5, &SSL::SetInterfaceVersion, "SetInterfaceVersion"},
{6, nullptr, "FlushSessionCache"},
};
// clang-format on
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ISslContext>();
}
RegisterHandlers(functions);
}
SSL::SSL() : ServiceFramework("ssl") {
static const FunctionInfo functions[] = {
{0, &SSL::CreateContext, "CreateContext"},
{1, nullptr, "GetContextCount"},
{2, nullptr, "GetCertificates"},
{3, nullptr, "GetCertificateBufSize"},
{4, nullptr, "DebugIoctl"},
{5, &SSL::SetInterfaceVersion, "SetInterfaceVersion"},
{6, nullptr, "FlushSessionCache"},
};
RegisterHandlers(functions);
}
private:
void CreateContext(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_SSL, "(STUBBED) called");
void SSL::SetInterfaceVersion(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_SSL, "(STUBBED) called");
IPC::RequestParser rp{ctx};
u32 unk1 = rp.Pop<u32>(); // Probably minor/major?
u32 unk2 = rp.Pop<u32>(); // TODO(ogniK): Figure out what this does
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ISslContext>();
}
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void SetInterfaceVersion(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_SSL, "(STUBBED) called");
IPC::RequestParser rp{ctx};
u32 unk1 = rp.Pop<u32>(); // Probably minor/major?
u32 unk2 = rp.Pop<u32>(); // TODO(ogniK): Figure out what this does
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
};
void InstallInterfaces(SM::ServiceManager& service_manager) {
std::make_shared<SSL>()->InstallAsService(service_manager);
+3 -11
View File
@@ -4,20 +4,12 @@
#pragma once
#include "core/hle/service/service.h"
namespace Service::SM {
class ServiceManager;
}
namespace Service::SSL {
class SSL final : public ServiceFramework<SSL> {
public:
explicit SSL();
~SSL() = default;
private:
void CreateContext(Kernel::HLERequestContext& ctx);
void SetInterfaceVersion(Kernel::HLERequestContext& ctx);
};
/// Registers all SSL services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager);
@@ -9,8 +9,10 @@
#include "core/core.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/romfs_factory.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/service/filesystem/filesystem.h"
@@ -20,10 +22,19 @@
namespace Loader {
AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file_)
: AppLoader(std::move(file_)) {
AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file_,
bool override_update)
: AppLoader(std::move(file_)), override_update(override_update) {
const auto dir = file->GetContainingDirectory();
// Title ID
const auto npdm = dir->GetFile("main.npdm");
if (npdm != nullptr) {
const auto res = metadata.Load(npdm);
if (res == ResultStatus::Success)
title_id = metadata.GetTitleID();
}
// Icon
FileSys::VirtualFile icon_file = nullptr;
for (const auto& language : FileSys::LANGUAGE_NAMES) {
@@ -60,14 +71,14 @@ AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys
if (nacp_file != nullptr) {
FileSys::NACP nacp(nacp_file);
title_id = nacp.GetTitleId();
name = nacp.GetApplicationName();
}
}
AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(
FileSys::VirtualDir directory)
: AppLoader(directory->GetFile("main")), dir(std::move(directory)) {}
FileSys::VirtualDir directory, bool override_update)
: AppLoader(directory->GetFile("main")), dir(std::move(directory)),
override_update(override_update) {}
FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::VirtualFile& file) {
if (FileSys::IsDirectoryExeFS(file->GetContainingDirectory())) {
@@ -89,7 +100,8 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
dir = file->GetContainingDirectory();
}
const FileSys::VirtualFile npdm = dir->GetFile("main.npdm");
// Read meta to determine title ID
FileSys::VirtualFile npdm = dir->GetFile("main.npdm");
if (npdm == nullptr)
return ResultStatus::ErrorMissingNPDM;
@@ -97,6 +109,21 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
if (result != ResultStatus::Success) {
return result;
}
if (override_update) {
const FileSys::PatchManager patch_manager(metadata.GetTitleID());
dir = patch_manager.PatchExeFS(dir);
}
// Reread in case PatchExeFS affected the main.npdm
npdm = dir->GetFile("main.npdm");
if (npdm == nullptr)
return ResultStatus::ErrorMissingNPDM;
ResultStatus result2 = metadata.Load(npdm);
if (result2 != ResultStatus::Success) {
return result2;
}
metadata.Print();
const FileSys::ProgramAddressSpaceType arch_bits{metadata.GetAddressSpaceType()};
@@ -158,8 +185,6 @@ ResultStatus AppLoader_DeconstructedRomDirectory::ReadIcon(std::vector<u8>& buff
}
ResultStatus AppLoader_DeconstructedRomDirectory::ReadProgramId(u64& out_program_id) {
if (name.empty())
return ResultStatus::ErrorNoControl;
out_program_id = title_id;
return ResultStatus::Success;
}
@@ -171,4 +196,8 @@ ResultStatus AppLoader_DeconstructedRomDirectory::ReadTitle(std::string& title)
return ResultStatus::Success;
}
bool AppLoader_DeconstructedRomDirectory::IsRomFSUpdatable() const {
return false;
}
} // namespace Loader
@@ -20,10 +20,12 @@ namespace Loader {
*/
class AppLoader_DeconstructedRomDirectory final : public AppLoader {
public:
explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile main_file);
explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile main_file,
bool override_update = false);
// Overload to accept exefs directory. Must contain 'main' and 'main.npdm'
explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualDir directory);
explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualDir directory,
bool override_update = false);
/**
* Returns the type of the file
@@ -42,6 +44,7 @@ public:
ResultStatus ReadIcon(std::vector<u8>& buffer) override;
ResultStatus ReadProgramId(u64& out_program_id) override;
ResultStatus ReadTitle(std::string& title) override;
bool IsRomFSUpdatable() const override;
private:
FileSys::ProgramMetadata metadata;
@@ -51,6 +54,7 @@ private:
std::vector<u8> icon_data;
std::string name;
u64 title_id{};
bool override_update;
};
} // namespace Loader
+1
View File
@@ -10,6 +10,7 @@
#include "common/file_util.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/loader/elf.h"
+24 -2
View File
@@ -5,9 +5,9 @@
#include <memory>
#include <ostream>
#include <string>
#include "common/file_util.h"
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/file_sys/vfs_real.h"
#include "core/hle/kernel/process.h"
#include "core/loader/deconstructed_rom_directory.h"
#include "core/loader/elf.h"
@@ -15,6 +15,7 @@
#include "core/loader/nca.h"
#include "core/loader/nro.h"
#include "core/loader/nso.h"
#include "core/loader/nsp.h"
#include "core/loader/xci.h"
namespace Loader {
@@ -34,6 +35,7 @@ FileType IdentifyFile(FileSys::VirtualFile file) {
CHECK_TYPE(NCA)
CHECK_TYPE(XCI)
CHECK_TYPE(NAX)
CHECK_TYPE(NSP)
#undef CHECK_TYPE
@@ -59,6 +61,8 @@ FileType GuessFromFilename(const std::string& name) {
return FileType::NCA;
if (extension == "xci")
return FileType::XCI;
if (extension == "nsp")
return FileType::NSP;
return FileType::Unknown;
}
@@ -77,6 +81,8 @@ std::string GetFileTypeString(FileType type) {
return "XCI";
case FileType::NAX:
return "NAX";
case FileType::NSP:
return "NSP";
case FileType::DeconstructedRomDirectory:
return "Directory";
case FileType::Error:
@@ -87,7 +93,7 @@ std::string GetFileTypeString(FileType type) {
return "unknown";
}
constexpr std::array<const char*, 49> RESULT_MESSAGES{
constexpr std::array<const char*, 58> RESULT_MESSAGES{
"The operation completed successfully.",
"The loader requested to load is already loaded.",
"The operation is not implemented.",
@@ -137,6 +143,15 @@ constexpr std::array<const char*, 49> RESULT_MESSAGES{
"The AES Key Generation Source could not be found.",
"The SD Save Key Source could not be found.",
"The SD NCA Key Source could not be found.",
"The NSP file is missing a Program-type NCA.",
"The BKTR-type NCA has a bad BKTR header.",
"The BKTR Subsection entry is not located immediately after the Relocation entry.",
"The BKTR Subsection entry is not at the end of the media block.",
"The BKTR-type NCA has a bad Relocation block.",
"The BKTR-type NCA has a bad Subsection block.",
"The BKTR-type NCA has a bad Relocation bucket.",
"The BKTR-type NCA has a bad Subsection bucket.",
"The BKTR-type NCA is missing the base RomFS.",
};
std::ostream& operator<<(std::ostream& os, ResultStatus status) {
@@ -144,6 +159,9 @@ std::ostream& operator<<(std::ostream& os, ResultStatus status) {
return os;
}
AppLoader::AppLoader(FileSys::VirtualFile file) : file(std::move(file)) {}
AppLoader::~AppLoader() = default;
/**
* Get a loader for a file with a specific type
* @param file The file to load
@@ -179,6 +197,10 @@ static std::unique_ptr<AppLoader> GetFileLoader(FileSys::VirtualFile file, FileT
case FileType::NAX:
return std::make_unique<AppLoader_NAX>(std::move(file));
// NX NSP (Nintendo Submission Package) file format
case FileType::NSP:
return std::make_unique<AppLoader_NSP>(std::move(file));
// NX deconstructed ROM directory.
case FileType::DeconstructedRomDirectory:
return std::make_unique<AppLoader_DeconstructedRomDirectory>(std::move(file));
+27 -10
View File
@@ -4,7 +4,6 @@
#pragma once
#include <algorithm>
#include <iosfwd>
#include <memory>
#include <string>
@@ -12,7 +11,6 @@
#include <vector>
#include <boost/optional.hpp>
#include "common/common_types.h"
#include "common/file_util.h"
#include "core/file_sys/vfs.h"
#include "core/hle/kernel/object.h"
@@ -31,6 +29,7 @@ enum class FileType {
NSO,
NRO,
NCA,
NSP,
XCI,
NAX,
DeconstructedRomDirectory,
@@ -107,6 +106,15 @@ enum class ResultStatus : u16 {
ErrorMissingAESKeyGenerationSource,
ErrorMissingSDSaveKeySource,
ErrorMissingSDNCAKeySource,
ErrorNSPMissingProgramNCA,
ErrorBadBKTRHeader,
ErrorBKTRSubsectionNotAfterRelocation,
ErrorBKTRSubsectionNotAtEnd,
ErrorBadRelocationBlock,
ErrorBadSubsectionBlock,
ErrorBadRelocationBuckets,
ErrorBadSubsectionBuckets,
ErrorMissingBKTRBaseRomFS,
};
std::ostream& operator<<(std::ostream& os, ResultStatus status);
@@ -114,8 +122,8 @@ std::ostream& operator<<(std::ostream& os, ResultStatus status);
/// Interface for loading an application
class AppLoader : NonCopyable {
public:
explicit AppLoader(FileSys::VirtualFile file) : file(std::move(file)) {}
virtual ~AppLoader() {}
explicit AppLoader(FileSys::VirtualFile file);
virtual ~AppLoader();
/**
* Returns the type of this file
@@ -197,13 +205,22 @@ public:
}
/**
* Get the update RomFS of the application
* Since the RomFS can be huge, we return a file reference instead of copying to a buffer
* @param file The file containing the RomFS
* @return ResultStatus result of function
* Get whether or not updates can be applied to the RomFS.
* By default, this is true, however for formats where it cannot be guaranteed that the RomFS is
* the base game it should be set to false.
* @return bool whether or not updatable.
*/
virtual ResultStatus ReadUpdateRomFS(FileSys::VirtualFile& file) {
return ResultStatus::ErrorNotImplemented;
virtual bool IsRomFSUpdatable() const {
return true;
}
/**
* Gets the difference between the start of the IVFC header and the start of level 6 (RomFS)
* data. Needed for bktr patching.
* @return IVFC offset for romfs.
*/
virtual u64 ReadRomFSIVFCOffset() const {
return 0;
}
/**
+7 -1
View File
@@ -48,7 +48,7 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) {
if (exefs == nullptr)
return ResultStatus::ErrorNoExeFS;
directory_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(exefs);
directory_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(exefs, true);
const auto load_result = directory_loader->Load(process);
if (load_result != ResultStatus::Success)
@@ -71,6 +71,12 @@ ResultStatus AppLoader_NCA::ReadRomFS(FileSys::VirtualFile& dir) {
return ResultStatus::Success;
}
u64 AppLoader_NCA::ReadRomFSIVFCOffset() const {
if (nca == nullptr)
return 0;
return nca->GetBaseIVFCOffset();
}
ResultStatus AppLoader_NCA::ReadProgramId(u64& out_program_id) {
if (nca == nullptr || nca->GetStatus() != ResultStatus::Success)
return ResultStatus::ErrorNotInitialized;
+1
View File
@@ -37,6 +37,7 @@ public:
ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
u64 ReadRomFSIVFCOffset() const override;
ResultStatus ReadProgramId(u64& out_program_id) override;
private:
+6
View File
@@ -14,6 +14,7 @@
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/vfs_offset.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/loader/nro.h"
@@ -231,4 +232,9 @@ ResultStatus AppLoader_NRO::ReadTitle(std::string& title) {
title = nacp->GetApplicationName();
return ResultStatus::Success;
}
bool AppLoader_NRO::IsRomFSUpdatable() const {
return false;
}
} // namespace Loader
+1
View File
@@ -39,6 +39,7 @@ public:
ResultStatus ReadProgramId(u64& out_program_id) override;
ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
ResultStatus ReadTitle(std::string& title) override;
bool IsRomFSUpdatable() const override;
private:
bool LoadNro(FileSys::VirtualFile file, VAddr load_base);
+1
View File
@@ -11,6 +11,7 @@
#include "common/swap.h"
#include "core/core.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/loader/nso.h"
+125
View File
@@ -0,0 +1,125 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <vector>
#include "common/common_types.h"
#include "core/file_sys/card_image.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs.h"
#include "core/file_sys/submission_package.h"
#include "core/hle/kernel/process.h"
#include "core/loader/deconstructed_rom_directory.h"
#include "core/loader/nca.h"
#include "core/loader/nsp.h"
namespace Loader {
AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file)
: AppLoader(file), nsp(std::make_unique<FileSys::NSP>(file)),
title_id(nsp->GetProgramTitleID()) {
if (nsp->GetStatus() != ResultStatus::Success)
return;
if (nsp->IsExtractedType())
return;
const auto control_nca =
nsp->GetNCA(nsp->GetProgramTitleID(), FileSys::ContentRecordType::Control);
if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success)
return;
std::tie(nacp_file, icon_file) =
FileSys::PatchManager(nsp->GetProgramTitleID()).ParseControlNCA(control_nca);
}
AppLoader_NSP::~AppLoader_NSP() = default;
FileType AppLoader_NSP::IdentifyType(const FileSys::VirtualFile& file) {
FileSys::NSP nsp(file);
if (nsp.GetStatus() == ResultStatus::Success) {
// Extracted Type case
if (nsp.IsExtractedType() && nsp.GetExeFS() != nullptr &&
FileSys::IsDirectoryExeFS(nsp.GetExeFS()) && nsp.GetRomFS() != nullptr) {
return FileType::NSP;
}
// Non-Ectracted Type case
if (!nsp.IsExtractedType() &&
nsp.GetNCA(nsp.GetFirstTitleID(), FileSys::ContentRecordType::Program) != nullptr &&
AppLoader_NCA::IdentifyType(nsp.GetNCAFile(
nsp.GetFirstTitleID(), FileSys::ContentRecordType::Program)) == FileType::NCA) {
return FileType::NSP;
}
}
return FileType::Error;
}
ResultStatus AppLoader_NSP::Load(Kernel::SharedPtr<Kernel::Process>& process) {
if (is_loaded) {
return ResultStatus::ErrorAlreadyLoaded;
}
if (nsp->IsExtractedType()) {
secondary_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(nsp->GetExeFS());
} else {
if (title_id == 0)
return ResultStatus::ErrorNSPMissingProgramNCA;
secondary_loader = std::make_unique<AppLoader_NCA>(
nsp->GetNCAFile(title_id, FileSys::ContentRecordType::Program));
if (nsp->GetStatus() != ResultStatus::Success)
return nsp->GetStatus();
if (nsp->GetProgramStatus(title_id) != ResultStatus::Success)
return nsp->GetProgramStatus(title_id);
if (nsp->GetNCA(title_id, FileSys::ContentRecordType::Program) == nullptr) {
if (!Core::Crypto::KeyManager::KeyFileExists(false))
return ResultStatus::ErrorMissingProductionKeyFile;
return ResultStatus::ErrorNSPMissingProgramNCA;
}
}
const auto result = secondary_loader->Load(process);
if (result != ResultStatus::Success)
return result;
is_loaded = true;
return ResultStatus::Success;
}
ResultStatus AppLoader_NSP::ReadRomFS(FileSys::VirtualFile& dir) {
return secondary_loader->ReadRomFS(dir);
}
ResultStatus AppLoader_NSP::ReadProgramId(u64& out_program_id) {
if (title_id == 0)
return ResultStatus::ErrorNotInitialized;
out_program_id = title_id;
return ResultStatus::Success;
}
ResultStatus AppLoader_NSP::ReadIcon(std::vector<u8>& buffer) {
if (icon_file == nullptr)
return ResultStatus::ErrorNoControl;
buffer = icon_file->ReadAllBytes();
return ResultStatus::Success;
}
ResultStatus AppLoader_NSP::ReadTitle(std::string& title) {
if (nacp_file == nullptr)
return ResultStatus::ErrorNoControl;
title = nacp_file->GetApplicationName();
return ResultStatus::Success;
}
} // namespace Loader
+54
View File
@@ -0,0 +1,54 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
#include "common/common_types.h"
#include "core/file_sys/vfs.h"
#include "core/loader/loader.h"
namespace FileSys {
class NACP;
class NSP;
} // namespace FileSys
namespace Loader {
class AppLoader_NCA;
/// Loads an XCI file
class AppLoader_NSP final : public AppLoader {
public:
explicit AppLoader_NSP(FileSys::VirtualFile file);
~AppLoader_NSP() override;
/**
* Returns the type of the file
* @param file std::shared_ptr<VfsFile> open file
* @return FileType found, or FileType::Error if this loader doesn't know it
*/
static FileType IdentifyType(const FileSys::VirtualFile& file);
FileType GetFileType() override {
return IdentifyType(file);
}
ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
ResultStatus ReadProgramId(u64& out_program_id) override;
ResultStatus ReadIcon(std::vector<u8>& buffer) override;
ResultStatus ReadTitle(std::string& title) override;
private:
std::unique_ptr<FileSys::NSP> nsp;
std::unique_ptr<AppLoader> secondary_loader;
FileSys::VirtualFile icon_file;
std::shared_ptr<FileSys::NACP> nacp_file;
u64 title_id;
};
} // namespace Loader
+9 -16
View File
@@ -8,7 +8,9 @@
#include "core/file_sys/card_image.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/romfs.h"
#include "core/file_sys/submission_package.h"
#include "core/hle/kernel/process.h"
#include "core/loader/nca.h"
#include "core/loader/xci.h"
@@ -17,25 +19,16 @@ namespace Loader {
AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file)
: AppLoader(file), xci(std::make_unique<FileSys::XCI>(file)),
nca_loader(std::make_unique<AppLoader_NCA>(
xci->GetNCAFileByType(FileSys::NCAContentType::Program))) {
nca_loader(std::make_unique<AppLoader_NCA>(xci->GetProgramNCAFile())) {
if (xci->GetStatus() != ResultStatus::Success)
return;
const auto control_nca = xci->GetNCAByType(FileSys::NCAContentType::Control);
if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success)
return;
const auto romfs = FileSys::ExtractRomFS(control_nca->GetRomFS());
if (romfs == nullptr)
return;
for (const auto& language : FileSys::LANGUAGE_NAMES) {
icon_file = romfs->GetFile("icon_" + std::string(language) + ".dat");
if (icon_file != nullptr)
break;
}
const auto nacp_raw = romfs->GetFile("control.nacp");
if (nacp_raw == nullptr)
return;
nacp_file = std::make_shared<FileSys::NACP>(nacp_raw);
std::tie(nacp_file, icon_file) =
FileSys::PatchManager(xci->GetProgramTitleID()).ParseControlNCA(control_nca);
}
AppLoader_XCI::~AppLoader_XCI() = default;
@@ -64,11 +57,11 @@ ResultStatus AppLoader_XCI::Load(Kernel::SharedPtr<Kernel::Process>& process) {
if (xci->GetProgramNCAStatus() != ResultStatus::Success)
return xci->GetProgramNCAStatus();
const auto nca = xci->GetNCAFileByType(FileSys::NCAContentType::Program);
const auto nca = xci->GetProgramNCA();
if (nca == nullptr && !Core::Crypto::KeyManager::KeyFileExists(false))
return ResultStatus::ErrorMissingProductionKeyFile;
auto result = nca_loader->Load(process);
const auto result = nca_loader->Load(process);
if (result != ResultStatus::Success)
return result;
+15 -36
View File
@@ -251,8 +251,8 @@ std::string ReadCString(VAddr vaddr, std::size_t max_length) {
return string;
}
void RasterizerMarkRegionCached(Tegra::GPUVAddr gpu_addr, u64 size, bool cached) {
if (gpu_addr == 0) {
void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
if (vaddr == 0) {
return;
}
@@ -261,19 +261,8 @@ void RasterizerMarkRegionCached(Tegra::GPUVAddr gpu_addr, u64 size, bool cached)
// CPU pages, hence why we iterate on a CPU page basis (note: GPU page size is different). This
// assumes the specified GPU address region is contiguous as well.
u64 num_pages = ((gpu_addr + size - 1) >> PAGE_BITS) - (gpu_addr >> PAGE_BITS) + 1;
for (unsigned i = 0; i < num_pages; ++i, gpu_addr += PAGE_SIZE) {
boost::optional<VAddr> maybe_vaddr =
Core::System::GetInstance().GPU().MemoryManager().GpuToCpuAddress(gpu_addr);
// The GPU <-> CPU virtual memory mapping is not 1:1
if (!maybe_vaddr) {
LOG_ERROR(HW_Memory,
"Trying to flush a cached region to an invalid physical address {:016X}",
gpu_addr);
continue;
}
VAddr vaddr = *maybe_vaddr;
u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1;
for (unsigned i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) {
PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS];
if (cached) {
@@ -344,29 +333,19 @@ void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode) {
const VAddr overlap_start = std::max(start, region_start);
const VAddr overlap_end = std::min(end, region_end);
const std::vector<Tegra::GPUVAddr> gpu_addresses =
system_instance.GPU().MemoryManager().CpuToGpuAddress(overlap_start);
if (gpu_addresses.empty()) {
return;
}
const u64 overlap_size = overlap_end - overlap_start;
for (const auto& gpu_address : gpu_addresses) {
auto& rasterizer = system_instance.Renderer().Rasterizer();
switch (mode) {
case FlushMode::Flush:
rasterizer.FlushRegion(gpu_address, overlap_size);
break;
case FlushMode::Invalidate:
rasterizer.InvalidateRegion(gpu_address, overlap_size);
break;
case FlushMode::FlushAndInvalidate:
rasterizer.FlushAndInvalidateRegion(gpu_address, overlap_size);
break;
}
auto& rasterizer = system_instance.Renderer().Rasterizer();
switch (mode) {
case FlushMode::Flush:
rasterizer.FlushRegion(overlap_start, overlap_size);
break;
case FlushMode::Invalidate:
rasterizer.InvalidateRegion(overlap_start, overlap_size);
break;
case FlushMode::FlushAndInvalidate:
rasterizer.FlushAndInvalidateRegion(overlap_start, overlap_size);
break;
}
};
+1 -2
View File
@@ -11,7 +11,6 @@
#include <boost/icl/interval_map.hpp>
#include "common/common_types.h"
#include "core/memory_hook.h"
#include "video_core/memory_manager.h"
namespace Kernel {
class Process;
@@ -179,7 +178,7 @@ enum class FlushMode {
/**
* Mark each page touching the region as cached.
*/
void RasterizerMarkRegionCached(Tegra::GPUVAddr gpu_addr, u64 size, bool cached);
void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached);
/**
* Flushes and invalidates any externally cached rasterizer resources touching the given virtual
+2 -2
View File
@@ -40,7 +40,7 @@ void PerfStats::EndGameFrame() {
game_frames += 1;
}
PerfStats::Results PerfStats::GetAndResetStats(microseconds current_system_time_us) {
PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us) {
std::lock_guard<std::mutex> lock(object_mutex);
const auto now = Clock::now();
@@ -49,7 +49,7 @@ PerfStats::Results PerfStats::GetAndResetStats(microseconds current_system_time_
const auto system_us_per_second = (current_system_time_us - reset_point_system_us) / interval;
Results results{};
PerfStatsResults results{};
results.system_fps = static_cast<double>(system_frames) / interval;
results.game_fps = static_cast<double>(game_frames) / interval;
results.frametime = duration_cast<DoubleSecs>(accumulated_frametime).count() /
+12 -12
View File
@@ -10,6 +10,17 @@
namespace Core {
struct PerfStatsResults {
/// System FPS (LCD VBlanks) in Hz
double system_fps;
/// Game FPS (GSP frame submissions) in Hz
double game_fps;
/// Walltime per system frame, in seconds, excluding any waits
double frametime;
/// Ratio of walltime / emulated time elapsed
double emulation_speed;
};
/**
* Class to manage and query performance/timing statistics. All public functions of this class are
* thread-safe unless stated otherwise.
@@ -18,22 +29,11 @@ class PerfStats {
public:
using Clock = std::chrono::high_resolution_clock;
struct Results {
/// System FPS (LCD VBlanks) in Hz
double system_fps;
/// Game FPS (GSP frame submissions) in Hz
double game_fps;
/// Walltime per system frame, in seconds, excluding any waits
double frametime;
/// Ratio of walltime / emulated time elapsed
double emulation_speed;
};
void BeginSystemFrame();
void EndSystemFrame();
void EndGameFrame();
Results GetAndResetStats(std::chrono::microseconds current_system_time_us);
PerfStatsResults GetAndResetStats(std::chrono::microseconds current_system_time_us);
/**
* Gets the ratio between walltime and the emulated time of the previous system frame. This is
+2
View File
@@ -127,6 +127,8 @@ struct Values {
// Data Storage
bool use_virtual_sd;
std::string nand_dir;
std::string sdmc_dir;
// Renderer
float resolution_factor;
+22 -3
View File
@@ -7,6 +7,9 @@
#include "common/file_util.h"
#include "core/core.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/loader/loader.h"
#include "core/settings.h"
#include "core/telemetry_session.h"
@@ -87,12 +90,28 @@ TelemetrySession::TelemetrySession() {
std::chrono::system_clock::now().time_since_epoch())
.count()};
AddField(Telemetry::FieldType::Session, "Init_Time", init_time);
std::string program_name;
const Loader::ResultStatus res{System::GetInstance().GetAppLoader().ReadTitle(program_name)};
u64 program_id{};
const Loader::ResultStatus res{System::GetInstance().GetAppLoader().ReadProgramId(program_id)};
if (res == Loader::ResultStatus::Success) {
AddField(Telemetry::FieldType::Session, "ProgramName", program_name);
AddField(Telemetry::FieldType::Session, "ProgramId", program_id);
std::string name;
System::GetInstance().GetAppLoader().ReadTitle(name);
if (name.empty()) {
auto [nacp, icon_file] = FileSys::PatchManager(program_id).GetControlMetadata();
if (nacp != nullptr)
name = nacp->GetApplicationName();
}
if (!name.empty())
AddField(Telemetry::FieldType::Session, "ProgramName", name);
}
AddField(Telemetry::FieldType::Session, "ProgramFormat",
static_cast<u8>(System::GetInstance().GetAppLoader().GetFileType()));
// Log application information
Telemetry::AppendBuildInfo(field_collection);
+1
View File
@@ -22,6 +22,7 @@ add_library(video_core STATIC
rasterizer_interface.h
renderer_base.cpp
renderer_base.h
renderer_opengl/gl_buffer_cache.cpp
renderer_opengl/gl_rasterizer.cpp
renderer_opengl/gl_rasterizer.h
renderer_opengl/gl_rasterizer_cache.cpp
+2 -2
View File
@@ -34,6 +34,8 @@ void GPU::WriteReg(u32 method, u32 subchannel, u32 value, u32 remaining_params)
"{:08X} remaining params {}",
method, subchannel, value, remaining_params);
ASSERT(subchannel < bound_engines.size());
if (method == static_cast<u32>(BufferMethods::BindObject)) {
// Bind the current subchannel to the desired engine id.
LOG_DEBUG(HW_GPU, "Binding subchannel {} to engine {}", subchannel, value);
@@ -47,8 +49,6 @@ void GPU::WriteReg(u32 method, u32 subchannel, u32 value, u32 remaining_params)
return;
}
ASSERT(bound_engines.find(subchannel) != bound_engines.end());
const EngineID engine = bound_engines[subchannel];
switch (engine) {
+4 -4
View File
@@ -5,13 +5,13 @@
#include <cinttypes>
#include "common/assert.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/memory.h"
#include "video_core/debug_utils/debug_utils.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/rasterizer_interface.h"
#include "video_core/renderer_base.h"
#include "video_core/textures/decoders.h"
#include "video_core/textures/texture.h"
#include "video_core/video_core.h"
namespace Tegra {
namespace Engines {
@@ -195,8 +195,8 @@ void Maxwell3D::ProcessQueryGet() {
// wait queues.
LongQueryResult query_result{};
query_result.value = result;
// TODO(Subv): Generate a real GPU timestamp and write it here instead of 0
query_result.timestamp = 0;
// TODO(Subv): Generate a real GPU timestamp and write it here instead of CoreTiming
query_result.timestamp = CoreTiming::GetTicks();
Memory::WriteBlock(*address, &query_result, sizeof(query_result));
}
break;
+7 -2
View File
@@ -127,6 +127,7 @@ public:
BitField<21, 6, Size> size;
BitField<27, 3, Type> type;
BitField<31, 1, u32> bgra;
u32 hex;
};
u32 ComponentCount() const {
@@ -262,6 +263,10 @@ public:
bool IsValid() const {
return size != Size::Invalid;
}
bool operator<(const VertexAttribute& other) const {
return hex < other.hex;
}
};
enum class PrimitiveTopology : u32 {
@@ -545,7 +550,7 @@ public:
INSERT_PADDING_WORDS(0x5B);
VertexAttribute vertex_attrib_format[NumVertexAttributes];
std::array<VertexAttribute, NumVertexAttributes> vertex_attrib_format;
INSERT_PADDING_WORDS(0xF);
@@ -964,7 +969,7 @@ ASSERT_REG_POSITION(stencil_back_func_ref, 0x3D5);
ASSERT_REG_POSITION(stencil_back_mask, 0x3D6);
ASSERT_REG_POSITION(stencil_back_func_mask, 0x3D7);
ASSERT_REG_POSITION(zeta, 0x3F8);
ASSERT_REG_POSITION(vertex_attrib_format[0], 0x458);
ASSERT_REG_POSITION(vertex_attrib_format, 0x458);
ASSERT_REG_POSITION(rt_control, 0x487);
ASSERT_REG_POSITION(zeta_width, 0x48a);
ASSERT_REG_POSITION(zeta_height, 0x48b);
+40
View File
@@ -76,6 +76,7 @@ union Attribute {
Position = 7,
Attribute_0 = 8,
Attribute_31 = 39,
PointCoord = 46,
// This attribute contains a tuple of (~, ~, InstanceId, VertexId) when inside a vertex
// shader, and a tuple of (TessCoord.x, TessCoord.y, TessCoord.z, ~) when inside a Tess Eval
// shader.
@@ -147,6 +148,7 @@ enum class PredCondition : u64 {
LessThanWithNan = 9,
GreaterThanWithNan = 12,
NotEqualWithNan = 13,
GreaterEqualWithNan = 14,
// TODO(Subv): Other condition types
};
@@ -242,6 +244,20 @@ enum class TextureType : u64 {
TextureCube = 3,
};
enum class IpaInterpMode : u64 { Linear = 0, Perspective = 1, Flat = 2, Sc = 3 };
enum class IpaSampleMode : u64 { Default = 0, Centroid = 1, Offset = 2 };
struct IpaMode {
IpaInterpMode interpolation_mode;
IpaSampleMode sampling_mode;
inline bool operator==(const IpaMode& a) {
return (a.interpolation_mode == interpolation_mode) && (a.sampling_mode == sampling_mode);
}
inline bool operator!=(const IpaMode& a) {
return !((*this) == a);
}
};
union Instruction {
Instruction& operator=(const Instruction& instr) {
value = instr.value;
@@ -325,6 +341,16 @@ union Instruction {
} alu;
union {
BitField<51, 1, u64> saturate;
BitField<52, 2, IpaSampleMode> sample_mode;
BitField<54, 2, IpaInterpMode> interp_mode;
} ipa;
union {
BitField<39, 2, u64> tab5cb8_2;
BitField<41, 3, u64> tab5c68_1;
BitField<44, 2, u64> tab5c68_0;
BitField<47, 1, u64> cc;
BitField<48, 1, u64> negate_b;
} fmul;
@@ -338,6 +364,10 @@ union Instruction {
BitField<49, 1, u64> negate_a;
} alu_integer;
union {
BitField<40, 1, u64> invert;
} popc;
union {
BitField<39, 3, u64> pred;
BitField<42, 1, u64> neg_pred;
@@ -388,8 +418,11 @@ union Instruction {
} flow;
union {
BitField<47, 1, u64> cc;
BitField<48, 1, u64> negate_b;
BitField<49, 1, u64> negate_c;
BitField<51, 2, u64> tab5980_1;
BitField<53, 2, u64> tab5980_0;
} ffma;
union {
@@ -498,6 +531,7 @@ union Instruction {
union {
BitField<0, 8, Register> gpr0;
BitField<28, 8, Register> gpr28;
BitField<49, 1, u64> nodep;
BitField<50, 3, u64> component_mask_selector;
BitField<53, 4, u64> texture_info;
@@ -665,6 +699,9 @@ public:
ISCADD_C, // Scale and Add
ISCADD_R,
ISCADD_IMM,
POPC_C,
POPC_R,
POPC_IMM,
SEL_C,
SEL_R,
SEL_IMM,
@@ -886,6 +923,9 @@ private:
INST("0100110000011---", Id::ISCADD_C, Type::ArithmeticInteger, "ISCADD_C"),
INST("0101110000011---", Id::ISCADD_R, Type::ArithmeticInteger, "ISCADD_R"),
INST("0011100-00011---", Id::ISCADD_IMM, Type::ArithmeticInteger, "ISCADD_IMM"),
INST("0100110000001---", Id::POPC_C, Type::ArithmeticInteger, "POPC_C"),
INST("0101110000001---", Id::POPC_R, Type::ArithmeticInteger, "POPC_R"),
INST("0011100-00001---", Id::POPC_IMM, Type::ArithmeticInteger, "POPC_IMM"),
INST("0100110010100---", Id::SEL_C, Type::ArithmeticInteger, "SEL_C"),
INST("0101110010100---", Id::SEL_R, Type::ArithmeticInteger, "SEL_R"),
INST("0011100-10100---", Id::SEL_IMM, Type::ArithmeticInteger, "SEL_IMM"),
+2 -2
View File
@@ -4,8 +4,8 @@
#pragma once
#include <array>
#include <memory>
#include <unordered_map>
#include "common/common_types.h"
#include "core/hle/service/nvflinger/buffer_queue.h"
#include "video_core/memory_manager.h"
@@ -136,7 +136,7 @@ private:
std::unique_ptr<Tegra::MemoryManager> memory_manager;
/// Mapping of command subchannels to their bound engine ids.
std::unordered_map<u32, EngineID> bound_engines;
std::array<EngineID, 8> bound_engines = {};
/// 3D engine
std::unique_ptr<Engines::Maxwell3D> maxwell_3d;
+50 -77
View File
@@ -4,113 +4,86 @@
#pragma once
#include <unordered_map>
#include <set>
#include <boost/icl/interval_map.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/range/iterator_range_core.hpp>
#include "common/common_types.h"
#include "core/memory.h"
#include "video_core/memory_manager.h"
#include "core/core.h"
#include "video_core/rasterizer_interface.h"
#include "video_core/renderer_base.h"
template <class T>
class RasterizerCache : NonCopyable {
public:
/// Mark the specified region as being invalidated
void InvalidateRegion(Tegra::GPUVAddr region_addr, size_t region_size) {
for (auto iter = cached_objects.cbegin(); iter != cached_objects.cend();) {
const auto& object{iter->second};
void InvalidateRegion(VAddr addr, u64 size) {
if (size == 0)
return;
++iter;
const ObjectInterval interval{addr, addr + size};
for (auto& pair : boost::make_iterator_range(object_cache.equal_range(interval))) {
for (auto& cached_object : pair.second) {
if (!cached_object)
continue;
if (object->GetAddr() <= (region_addr + region_size) &&
region_addr <= (object->GetAddr() + object->GetSizeInBytes())) {
// Regions overlap, so invalidate
Unregister(object);
remove_objects.emplace(cached_object);
}
}
for (auto& remove_object : remove_objects) {
Unregister(remove_object);
}
remove_objects.clear();
}
/// Invalidates everything in the cache
void InvalidateAll() {
while (object_cache.begin() != object_cache.end()) {
Unregister(*object_cache.begin()->second.begin());
}
}
protected:
/// Tries to get an object from the cache with the specified address
T TryGet(Tegra::GPUVAddr addr) const {
const auto& search{cached_objects.find(addr)};
if (search != cached_objects.end()) {
return search->second;
T TryGet(VAddr addr) const {
const ObjectInterval interval{addr};
for (auto& pair : boost::make_iterator_range(object_cache.equal_range(interval))) {
for (auto& cached_object : pair.second) {
if (cached_object->GetAddr() == addr) {
return cached_object;
}
}
}
return nullptr;
}
/// Gets a reference to the cache
const std::unordered_map<Tegra::GPUVAddr, T>& GetCache() const {
return cached_objects;
}
/// Register an object into the cache
void Register(const T& object) {
const auto& search{cached_objects.find(object->GetAddr())};
if (search != cached_objects.end()) {
// Registered already
return;
}
cached_objects[object->GetAddr()] = object;
UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), 1);
object_cache.add({GetInterval(object), ObjectSet{object}});
auto& rasterizer = Core::System::GetInstance().Renderer().Rasterizer();
rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), 1);
}
/// Unregisters an object from the cache
void Unregister(const T& object) {
const auto& search{cached_objects.find(object->GetAddr())};
if (search == cached_objects.end()) {
// Unregistered already
return;
}
UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), -1);
cached_objects.erase(search);
auto& rasterizer = Core::System::GetInstance().Renderer().Rasterizer();
rasterizer.UpdatePagesCachedCount(object->GetAddr(), object->GetSizeInBytes(), -1);
object_cache.subtract({GetInterval(object), ObjectSet{object}});
}
private:
using PageMap = boost::icl::interval_map<u64, int>;
using ObjectSet = std::set<T>;
using ObjectCache = boost::icl::interval_map<VAddr, ObjectSet>;
using ObjectInterval = typename ObjectCache::interval_type;
template <typename Map, typename Interval>
constexpr auto RangeFromInterval(Map& map, const Interval& interval) {
return boost::make_iterator_range(map.equal_range(interval));
static auto GetInterval(const T& object) {
return ObjectInterval::right_open(object->GetAddr(),
object->GetAddr() + object->GetSizeInBytes());
}
/// Increase/decrease the number of object in pages touching the specified region
void UpdatePagesCachedCount(Tegra::GPUVAddr addr, u64 size, int delta) {
const u64 page_start{addr >> Tegra::MemoryManager::PAGE_BITS};
const u64 page_end{(addr + size) >> Tegra::MemoryManager::PAGE_BITS};
// Interval maps will erase segments if count reaches 0, so if delta is negative we have to
// subtract after iterating
const auto pages_interval = PageMap::interval_type::right_open(page_start, page_end);
if (delta > 0)
cached_pages.add({pages_interval, delta});
for (const auto& pair : RangeFromInterval(cached_pages, pages_interval)) {
const auto interval = pair.first & pages_interval;
const int count = pair.second;
const Tegra::GPUVAddr interval_start_addr = boost::icl::first(interval)
<< Tegra::MemoryManager::PAGE_BITS;
const Tegra::GPUVAddr interval_end_addr = boost::icl::last_next(interval)
<< Tegra::MemoryManager::PAGE_BITS;
const u64 interval_size = interval_end_addr - interval_start_addr;
if (delta > 0 && count == delta)
Memory::RasterizerMarkRegionCached(interval_start_addr, interval_size, true);
else if (delta < 0 && count == -delta)
Memory::RasterizerMarkRegionCached(interval_start_addr, interval_size, false);
else
ASSERT(count >= 0);
}
if (delta < 0)
cached_pages.add({pages_interval, delta});
}
std::unordered_map<Tegra::GPUVAddr, T> cached_objects;
PageMap cached_pages;
ObjectCache object_cache;
ObjectSet remove_objects;
};
+6 -3
View File
@@ -27,14 +27,14 @@ public:
virtual void FlushAll() = 0;
/// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
virtual void FlushRegion(Tegra::GPUVAddr addr, u64 size) = 0;
virtual void FlushRegion(VAddr addr, u64 size) = 0;
/// Notify rasterizer that any caches of the specified region should be invalidated
virtual void InvalidateRegion(Tegra::GPUVAddr addr, u64 size) = 0;
virtual void InvalidateRegion(VAddr addr, u64 size) = 0;
/// Notify rasterizer that any caches of the specified region should be flushed to Switch memory
/// and invalidated
virtual void FlushAndInvalidateRegion(Tegra::GPUVAddr addr, u64 size) = 0;
virtual void FlushAndInvalidateRegion(VAddr addr, u64 size) = 0;
/// Attempt to use a faster method to perform a display transfer with is_texture_copy = 0
virtual bool AccelerateDisplayTransfer(const void* config) {
@@ -60,5 +60,8 @@ public:
virtual bool AccelerateDrawBatch(bool is_indexed) {
return false;
}
/// Increase/decrease the number of object in pages touching the specified region
virtual void UpdatePagesCachedCount(Tegra::GPUVAddr addr, u64 size, int delta) {}
};
} // namespace VideoCore
@@ -0,0 +1,92 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstring>
#include <memory>
#include "common/alignment.h"
#include "core/core.h"
#include "core/memory.h"
#include "video_core/renderer_opengl/gl_buffer_cache.h"
namespace OpenGL {
OGLBufferCache::OGLBufferCache(size_t size) : stream_buffer(GL_ARRAY_BUFFER, size) {}
GLintptr OGLBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, size_t size, size_t alignment,
bool cache) {
auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager();
const boost::optional<VAddr> cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)};
// Cache management is a big overhead, so only cache entries with a given size.
// TODO: Figure out which size is the best for given games.
cache &= size >= 2048;
if (cache) {
auto entry = TryGet(*cpu_addr);
if (entry) {
if (entry->size >= size && entry->alignment == alignment) {
return entry->offset;
}
Unregister(entry);
}
}
AlignBuffer(alignment);
GLintptr uploaded_offset = buffer_offset;
Memory::ReadBlock(*cpu_addr, buffer_ptr, size);
buffer_ptr += size;
buffer_offset += size;
if (cache) {
auto entry = std::make_shared<CachedBufferEntry>();
entry->offset = uploaded_offset;
entry->size = size;
entry->alignment = alignment;
entry->addr = *cpu_addr;
Register(entry);
}
return uploaded_offset;
}
GLintptr OGLBufferCache::UploadHostMemory(const void* raw_pointer, size_t size, size_t alignment) {
AlignBuffer(alignment);
std::memcpy(buffer_ptr, raw_pointer, size);
GLintptr uploaded_offset = buffer_offset;
buffer_ptr += size;
buffer_offset += size;
return uploaded_offset;
}
void OGLBufferCache::Map(size_t max_size) {
bool invalidate;
std::tie(buffer_ptr, buffer_offset_base, invalidate) =
stream_buffer.Map(static_cast<GLsizeiptr>(max_size), 4);
buffer_offset = buffer_offset_base;
if (invalidate) {
InvalidateAll();
}
}
void OGLBufferCache::Unmap() {
stream_buffer.Unmap(buffer_offset - buffer_offset_base);
}
GLuint OGLBufferCache::GetHandle() const {
return stream_buffer.GetHandle();
}
void OGLBufferCache::AlignBuffer(size_t alignment) {
// Align the offset, not the mapped pointer
GLintptr offset_aligned =
static_cast<GLintptr>(Common::AlignUp(static_cast<size_t>(buffer_offset), alignment));
buffer_ptr += offset_aligned - buffer_offset;
buffer_offset = offset_aligned;
}
} // namespace OpenGL
@@ -0,0 +1,57 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <cstddef>
#include <memory>
#include "common/common_types.h"
#include "video_core/rasterizer_cache.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
#include "video_core/renderer_opengl/gl_stream_buffer.h"
namespace OpenGL {
struct CachedBufferEntry final {
VAddr GetAddr() const {
return addr;
}
size_t GetSizeInBytes() const {
return size;
}
VAddr addr;
size_t size;
GLintptr offset;
size_t alignment;
};
class OGLBufferCache final : public RasterizerCache<std::shared_ptr<CachedBufferEntry>> {
public:
explicit OGLBufferCache(size_t size);
GLintptr UploadMemory(Tegra::GPUVAddr gpu_addr, size_t size, size_t alignment = 4,
bool cache = true);
GLintptr UploadHostMemory(const void* raw_pointer, size_t size, size_t alignment = 4);
void Map(size_t max_size);
void Unmap();
GLuint GetHandle() const;
protected:
void AlignBuffer(size_t alignment);
private:
OGLStreamBuffer stream_buffer;
u8* buffer_ptr = nullptr;
GLintptr buffer_offset = 0;
GLintptr buffer_offset_base = 0;
};
} // namespace OpenGL
+124 -148
View File
@@ -33,14 +33,17 @@ using PixelFormat = SurfaceParams::PixelFormat;
using SurfaceType = SurfaceParams::SurfaceType;
MICROPROFILE_DEFINE(OpenGL_VAO, "OpenGL", "Vertex Array Setup", MP_RGB(128, 128, 192));
MICROPROFILE_DEFINE(OpenGL_VS, "OpenGL", "Vertex Shader Setup", MP_RGB(128, 128, 192));
MICROPROFILE_DEFINE(OpenGL_FS, "OpenGL", "Fragment Shader Setup", MP_RGB(128, 128, 192));
MICROPROFILE_DEFINE(OpenGL_Shader, "OpenGL", "Shader Setup", MP_RGB(128, 128, 192));
MICROPROFILE_DEFINE(OpenGL_UBO, "OpenGL", "Const Buffer Setup", MP_RGB(128, 128, 192));
MICROPROFILE_DEFINE(OpenGL_Index, "OpenGL", "Index Buffer Setup", MP_RGB(128, 128, 192));
MICROPROFILE_DEFINE(OpenGL_Texture, "OpenGL", "Texture Setup", MP_RGB(128, 128, 192));
MICROPROFILE_DEFINE(OpenGL_Framebuffer, "OpenGL", "Framebuffer Setup", MP_RGB(128, 128, 192));
MICROPROFILE_DEFINE(OpenGL_Drawing, "OpenGL", "Drawing", MP_RGB(128, 128, 192));
MICROPROFILE_DEFINE(OpenGL_Blits, "OpenGL", "Blits", MP_RGB(100, 100, 255));
MICROPROFILE_DEFINE(OpenGL_Blits, "OpenGL", "Blits", MP_RGB(128, 128, 192));
MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100));
RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo& info)
: emu_window{window}, screen_info{info}, stream_buffer(GL_ARRAY_BUFFER, STREAM_BUFFER_SIZE) {
: emu_window{window}, screen_info{info}, buffer_cache(STREAM_BUFFER_SIZE) {
// Create sampler objects
for (size_t i = 0; i < texture_samplers.size(); ++i) {
texture_samplers[i].Create();
@@ -67,28 +70,13 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo
// Clipping plane 0 is always enabled for PICA fixed clip plane z <= 0
state.clip_distance[0] = true;
// Generate VAO and UBO
sw_vao.Create();
uniform_buffer.Create();
state.draw.vertex_array = sw_vao.handle;
state.draw.uniform_buffer = uniform_buffer.handle;
state.Apply();
// Create render framebuffer
framebuffer.Create();
hw_vao.Create();
state.draw.vertex_buffer = stream_buffer.GetHandle();
shader_program_manager = std::make_unique<GLShader::ProgramManager>();
state.draw.shader_program = 0;
state.draw.vertex_array = hw_vao.handle;
state.Apply();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, stream_buffer.GetHandle());
glEnable(GL_BLEND);
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uniform_buffer_alignment);
@@ -98,14 +86,60 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo
RasterizerOpenGL::~RasterizerOpenGL() {}
std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr,
GLintptr buffer_offset) {
void RasterizerOpenGL::SetupVertexArrays() {
MICROPROFILE_SCOPE(OpenGL_VAO);
const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
const auto& regs = gpu.regs;
state.draw.vertex_array = hw_vao.handle;
state.draw.vertex_buffer = stream_buffer.GetHandle();
auto [iter, is_cache_miss] = vertex_array_cache.try_emplace(regs.vertex_attrib_format);
auto& VAO = iter->second;
if (is_cache_miss) {
VAO.Create();
state.draw.vertex_array = VAO.handle;
state.Apply();
// The index buffer binding is stored within the VAO. Stupid OpenGL, but easy to work
// around.
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer_cache.GetHandle());
// Use the vertex array as-is, assumes that the data is formatted correctly for OpenGL.
// Enables the first 16 vertex attributes always, as we don't know which ones are actually
// used until shader time. Note, Tegra technically supports 32, but we're capping this to 16
// for now to avoid OpenGL errors.
// TODO(Subv): Analyze the shader to identify which attributes are actually used and don't
// assume every shader uses them all.
for (unsigned index = 0; index < 16; ++index) {
const auto& attrib = regs.vertex_attrib_format[index];
// Ignore invalid attributes.
if (!attrib.IsValid())
continue;
const auto& buffer = regs.vertex_array[attrib.buffer];
LOG_TRACE(HW_GPU,
"vertex attrib {}, count={}, size={}, type={}, offset={}, normalize={}",
index, attrib.ComponentCount(), attrib.SizeString(), attrib.TypeString(),
attrib.offset.Value(), attrib.IsNormalized());
ASSERT(buffer.IsEnabled());
glEnableVertexAttribArray(index);
if (attrib.type == Tegra::Engines::Maxwell3D::Regs::VertexAttribute::Type::SignedInt ||
attrib.type ==
Tegra::Engines::Maxwell3D::Regs::VertexAttribute::Type::UnsignedInt) {
glVertexAttribIFormat(index, attrib.ComponentCount(),
MaxwellToGL::VertexType(attrib), attrib.offset);
} else {
glVertexAttribFormat(index, attrib.ComponentCount(),
MaxwellToGL::VertexType(attrib),
attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset);
}
glVertexAttribBinding(index, attrib.buffer);
}
}
state.draw.vertex_array = VAO.handle;
state.draw.vertex_buffer = buffer_cache.GetHandle();
state.Apply();
// Upload all guest vertex arrays sequentially to our buffer
@@ -124,12 +158,10 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr,
ASSERT(end > start);
u64 size = end - start + 1;
GLintptr vertex_buffer_offset;
std::tie(array_ptr, buffer_offset, vertex_buffer_offset) =
UploadMemory(array_ptr, buffer_offset, start, size);
GLintptr vertex_buffer_offset = buffer_cache.UploadMemory(start, size);
// Bind the vertex array to the buffer at the current offset.
glBindVertexBuffer(index, stream_buffer.GetHandle(), vertex_buffer_offset,
glBindVertexBuffer(index, buffer_cache.GetHandle(), vertex_buffer_offset,
vertex_array.stride);
if (regs.instanced_arrays.IsInstancingEnabled(index) && vertex_array.divisor != 0) {
@@ -142,43 +174,10 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr,
glVertexBindingDivisor(index, 0);
}
}
// Use the vertex array as-is, assumes that the data is formatted correctly for OpenGL.
// Enables the first 16 vertex attributes always, as we don't know which ones are actually used
// until shader time. Note, Tegra technically supports 32, but we're capping this to 16 for now
// to avoid OpenGL errors.
// TODO(Subv): Analyze the shader to identify which attributes are actually used and don't
// assume every shader uses them all.
for (unsigned index = 0; index < 16; ++index) {
auto& attrib = regs.vertex_attrib_format[index];
// Ignore invalid attributes.
if (!attrib.IsValid())
continue;
auto& buffer = regs.vertex_array[attrib.buffer];
LOG_TRACE(HW_GPU, "vertex attrib {}, count={}, size={}, type={}, offset={}, normalize={}",
index, attrib.ComponentCount(), attrib.SizeString(), attrib.TypeString(),
attrib.offset.Value(), attrib.IsNormalized());
ASSERT(buffer.IsEnabled());
glEnableVertexAttribArray(index);
if (attrib.type == Tegra::Engines::Maxwell3D::Regs::VertexAttribute::Type::SignedInt ||
attrib.type == Tegra::Engines::Maxwell3D::Regs::VertexAttribute::Type::UnsignedInt) {
glVertexAttribIFormat(index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib),
attrib.offset);
} else {
glVertexAttribFormat(index, attrib.ComponentCount(), MaxwellToGL::VertexType(attrib),
attrib.IsNormalized() ? GL_TRUE : GL_FALSE, attrib.offset);
}
glVertexAttribBinding(index, attrib.buffer);
}
return {array_ptr, buffer_offset};
}
std::pair<u8*, GLintptr> RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) {
void RasterizerOpenGL::SetupShaders() {
MICROPROFILE_SCOPE(OpenGL_Shader);
auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
// Next available bindpoints to use when uploading the const buffers and textures to the GLSL
@@ -195,21 +194,15 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr
continue;
}
std::tie(buffer_ptr, buffer_offset) =
AlignBuffer(buffer_ptr, buffer_offset, static_cast<size_t>(uniform_buffer_alignment));
const size_t stage{index == 0 ? 0 : index - 1}; // Stage indices are 0 - 5
GLShader::MaxwellUniformData ubo{};
ubo.SetFromRegs(gpu.state.shader_stages[stage]);
std::memcpy(buffer_ptr, &ubo, sizeof(ubo));
GLintptr offset = buffer_cache.UploadHostMemory(
&ubo, sizeof(ubo), static_cast<size_t>(uniform_buffer_alignment));
// Bind the buffer
glBindBufferRange(GL_UNIFORM_BUFFER, stage, stream_buffer.GetHandle(), buffer_offset,
sizeof(ubo));
buffer_ptr += sizeof(ubo);
buffer_offset += sizeof(ubo);
glBindBufferRange(GL_UNIFORM_BUFFER, stage, buffer_cache.GetHandle(), offset, sizeof(ubo));
Shader shader{shader_cache.GetStageProgram(program)};
@@ -230,9 +223,8 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr
}
// Configure the const buffers for this shader stage.
std::tie(buffer_ptr, buffer_offset, current_constbuffer_bindpoint) =
SetupConstBuffers(buffer_ptr, buffer_offset, static_cast<Maxwell::ShaderStage>(stage),
shader, current_constbuffer_bindpoint);
current_constbuffer_bindpoint = SetupConstBuffers(static_cast<Maxwell::ShaderStage>(stage),
shader, current_constbuffer_bindpoint);
// Configure the textures for this shader stage.
current_texture_bindpoint = SetupTextures(static_cast<Maxwell::ShaderStage>(stage), shader,
@@ -246,8 +238,6 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr
}
shader_program_manager->UseTrivialGeometryShader();
return {buffer_ptr, buffer_offset};
}
size_t RasterizerOpenGL::CalculateVertexArraysSize() const {
@@ -274,9 +264,45 @@ bool RasterizerOpenGL::AccelerateDrawBatch(bool is_indexed) {
return true;
}
template <typename Map, typename Interval>
static constexpr auto RangeFromInterval(Map& map, const Interval& interval) {
return boost::make_iterator_range(map.equal_range(interval));
}
void RasterizerOpenGL::UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {
const u64 page_start{addr >> Memory::PAGE_BITS};
const u64 page_end{(addr + size + Memory::PAGE_SIZE - 1) >> Memory::PAGE_BITS};
// Interval maps will erase segments if count reaches 0, so if delta is negative we have to
// subtract after iterating
const auto pages_interval = CachedPageMap::interval_type::right_open(page_start, page_end);
if (delta > 0)
cached_pages.add({pages_interval, delta});
for (const auto& pair : RangeFromInterval(cached_pages, pages_interval)) {
const auto interval = pair.first & pages_interval;
const int count = pair.second;
const VAddr interval_start_addr = boost::icl::first(interval) << Memory::PAGE_BITS;
const VAddr interval_end_addr = boost::icl::last_next(interval) << Memory::PAGE_BITS;
const u64 interval_size = interval_end_addr - interval_start_addr;
if (delta > 0 && count == delta)
Memory::RasterizerMarkRegionCached(interval_start_addr, interval_size, true);
else if (delta < 0 && count == -delta)
Memory::RasterizerMarkRegionCached(interval_start_addr, interval_size, false);
else
ASSERT(count >= 0);
}
if (delta < 0)
cached_pages.add({pages_interval, delta});
}
std::pair<Surface, Surface> RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb,
bool using_depth_fb,
bool preserve_contents) {
MICROPROFILE_SCOPE(OpenGL_Framebuffer);
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
if (regs.rt[0].format == Tegra::RenderTargetFormat::NONE) {
@@ -397,41 +423,6 @@ void RasterizerOpenGL::Clear() {
glClearStencil(regs.clear_stencil);
glClear(clear_mask);
// Mark framebuffer surfaces as dirty
if (Settings::values.use_accurate_framebuffers) {
if (dirty_color_surface != nullptr) {
res_cache.FlushSurface(dirty_color_surface);
}
if (dirty_depth_surface != nullptr) {
res_cache.FlushSurface(dirty_depth_surface);
}
}
}
std::pair<u8*, GLintptr> RasterizerOpenGL::AlignBuffer(u8* buffer_ptr, GLintptr buffer_offset,
size_t alignment) {
// Align the offset, not the mapped pointer
GLintptr offset_aligned =
static_cast<GLintptr>(Common::AlignUp(static_cast<size_t>(buffer_offset), alignment));
return {buffer_ptr + (offset_aligned - buffer_offset), offset_aligned};
}
std::tuple<u8*, GLintptr, GLintptr> RasterizerOpenGL::UploadMemory(u8* buffer_ptr,
GLintptr buffer_offset,
Tegra::GPUVAddr gpu_addr,
size_t size, size_t alignment) {
std::tie(buffer_ptr, buffer_offset) = AlignBuffer(buffer_ptr, buffer_offset, alignment);
GLintptr uploaded_offset = buffer_offset;
auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager();
const boost::optional<VAddr> cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)};
Memory::ReadBlock(*cpu_addr, buffer_ptr, size);
buffer_ptr += size;
buffer_offset += size;
return {buffer_ptr, buffer_offset, uploaded_offset};
}
void RasterizerOpenGL::DrawArrays() {
@@ -459,7 +450,7 @@ void RasterizerOpenGL::DrawArrays() {
const bool is_indexed = accelerate_draw == AccelDraw::Indexed;
const u64 index_buffer_size{regs.index_array.count * regs.index_array.FormatSizeInBytes()};
state.draw.vertex_buffer = stream_buffer.GetHandle();
state.draw.vertex_buffer = buffer_cache.GetHandle();
state.Apply();
size_t buffer_size = CalculateVertexArraysSize();
@@ -476,24 +467,21 @@ void RasterizerOpenGL::DrawArrays() {
// Add space for at least 18 constant buffers
buffer_size += Maxwell::MaxConstBuffers * (MaxConstbufferSize + uniform_buffer_alignment);
u8* buffer_ptr;
GLintptr buffer_offset;
std::tie(buffer_ptr, buffer_offset, std::ignore) =
stream_buffer.Map(static_cast<GLsizeiptr>(buffer_size), 4);
u8* buffer_ptr_base = buffer_ptr;
buffer_cache.Map(buffer_size);
std::tie(buffer_ptr, buffer_offset) = SetupVertexArrays(buffer_ptr, buffer_offset);
SetupVertexArrays();
// If indexed mode, copy the index buffer
GLintptr index_buffer_offset = 0;
if (is_indexed) {
std::tie(buffer_ptr, buffer_offset, index_buffer_offset) = UploadMemory(
buffer_ptr, buffer_offset, regs.index_array.StartAddress(), index_buffer_size);
MICROPROFILE_SCOPE(OpenGL_Index);
index_buffer_offset =
buffer_cache.UploadMemory(regs.index_array.StartAddress(), index_buffer_size);
}
std::tie(buffer_ptr, buffer_offset) = SetupShaders(buffer_ptr, buffer_offset);
SetupShaders();
stream_buffer.Unmap(buffer_ptr - buffer_ptr_base);
buffer_cache.Unmap();
shader_program_manager->ApplyTo(state);
state.Apply();
@@ -522,16 +510,6 @@ void RasterizerOpenGL::DrawArrays() {
texture_unit.Unbind();
}
state.Apply();
// Mark framebuffer surfaces as dirty
if (Settings::values.use_accurate_framebuffers) {
if (dirty_color_surface != nullptr) {
res_cache.FlushSurface(dirty_color_surface);
}
if (dirty_depth_surface != nullptr) {
res_cache.FlushSurface(dirty_depth_surface);
}
}
}
void RasterizerOpenGL::NotifyMaxwellRegisterChanged(u32 method) {}
@@ -540,17 +518,18 @@ void RasterizerOpenGL::FlushAll() {
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
}
void RasterizerOpenGL::FlushRegion(Tegra::GPUVAddr addr, u64 size) {
void RasterizerOpenGL::FlushRegion(VAddr addr, u64 size) {
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
}
void RasterizerOpenGL::InvalidateRegion(Tegra::GPUVAddr addr, u64 size) {
void RasterizerOpenGL::InvalidateRegion(VAddr addr, u64 size) {
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
res_cache.InvalidateRegion(addr, size);
shader_cache.InvalidateRegion(addr, size);
buffer_cache.InvalidateRegion(addr, size);
}
void RasterizerOpenGL::FlushAndInvalidateRegion(Tegra::GPUVAddr addr, u64 size) {
void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) {
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
InvalidateRegion(addr, size);
}
@@ -637,11 +616,9 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr
}
}
std::tuple<u8*, GLintptr, u32> RasterizerOpenGL::SetupConstBuffers(u8* buffer_ptr,
GLintptr buffer_offset,
Maxwell::ShaderStage stage,
Shader& shader,
u32 current_bindpoint) {
u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shader,
u32 current_bindpoint) {
MICROPROFILE_SCOPE(OpenGL_UBO);
const auto& gpu = Core::System::GetInstance().GPU();
const auto& maxwell3d = gpu.Maxwell3D();
const auto& shader_stage = maxwell3d.state.shader_stages[static_cast<size_t>(stage)];
@@ -677,26 +654,25 @@ std::tuple<u8*, GLintptr, u32> RasterizerOpenGL::SetupConstBuffers(u8* buffer_pt
size = Common::AlignUp(size, sizeof(GLvec4));
ASSERT_MSG(size <= MaxConstbufferSize, "Constbuffer too big");
GLintptr const_buffer_offset;
std::tie(buffer_ptr, buffer_offset, const_buffer_offset) =
UploadMemory(buffer_ptr, buffer_offset, buffer.address, size,
static_cast<size_t>(uniform_buffer_alignment));
GLintptr const_buffer_offset = buffer_cache.UploadMemory(
buffer.address, size, static_cast<size_t>(uniform_buffer_alignment));
glBindBufferRange(GL_UNIFORM_BUFFER, current_bindpoint + bindpoint,
stream_buffer.GetHandle(), const_buffer_offset, size);
buffer_cache.GetHandle(), const_buffer_offset, size);
// Now configure the bindpoint of the buffer inside the shader
glUniformBlockBinding(shader->GetProgramHandle(),
shader->GetProgramResourceIndex(used_buffer.GetName()),
shader->GetProgramResourceIndex(used_buffer),
current_bindpoint + bindpoint);
}
state.Apply();
return {buffer_ptr, buffer_offset, current_bindpoint + static_cast<u32>(entries.size())};
return current_bindpoint + static_cast<u32>(entries.size());
}
u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader, u32 current_unit) {
MICROPROFILE_SCOPE(OpenGL_Texture);
const auto& gpu = Core::System::GetInstance().GPU();
const auto& maxwell3d = gpu.Maxwell3D();
const auto& entries = shader->GetShaderEntries().texture_samplers;
@@ -710,7 +686,7 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
// Bind the uniform to the sampler.
glProgramUniform1i(shader->GetProgramHandle(), shader->GetUniformLocation(entry.GetName()),
glProgramUniform1i(shader->GetProgramHandle(), shader->GetUniformLocation(entry),
current_bindpoint);
const auto texture = maxwell3d.GetStageTexture(entry.GetStage(), entry.GetOffset());
+23 -18
View File
@@ -6,15 +6,22 @@
#include <array>
#include <cstddef>
#include <map>
#include <memory>
#include <tuple>
#include <utility>
#include <vector>
#include <boost/icl/interval_map.hpp>
#include <boost/range/iterator_range.hpp>
#include <glad/glad.h>
#include "common/common_types.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/memory_manager.h"
#include "video_core/rasterizer_cache.h"
#include "video_core/rasterizer_interface.h"
#include "video_core/renderer_opengl/gl_buffer_cache.h"
#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
#include "video_core/renderer_opengl/gl_shader_cache.h"
@@ -40,15 +47,16 @@ public:
void Clear() override;
void NotifyMaxwellRegisterChanged(u32 method) override;
void FlushAll() override;
void FlushRegion(Tegra::GPUVAddr addr, u64 size) override;
void InvalidateRegion(Tegra::GPUVAddr addr, u64 size) override;
void FlushAndInvalidateRegion(Tegra::GPUVAddr addr, u64 size) override;
void FlushRegion(VAddr addr, u64 size) override;
void InvalidateRegion(VAddr addr, u64 size) override;
void FlushAndInvalidateRegion(VAddr addr, u64 size) override;
bool AccelerateDisplayTransfer(const void* config) override;
bool AccelerateTextureCopy(const void* config) override;
bool AccelerateFill(const void* config) override;
bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr,
u32 pixel_stride) override;
bool AccelerateDrawBatch(bool is_indexed) override;
void UpdatePagesCachedCount(Tegra::GPUVAddr addr, u64 size, int delta) override;
/// OpenGL shader generated for a given Maxwell register state
struct MaxwellShader {
@@ -104,9 +112,8 @@ private:
* @param current_bindpoint The offset at which to start counting new buffer bindpoints.
* @returns The next available bindpoint for use in the next shader stage.
*/
std::tuple<u8*, GLintptr, u32> SetupConstBuffers(
u8* buffer_ptr, GLintptr buffer_offset, Tegra::Engines::Maxwell3D::Regs::ShaderStage stage,
Shader& shader, u32 current_bindpoint);
u32 SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, Shader& shader,
u32 current_bindpoint);
/*
* Configures the current textures to use for the draw command.
@@ -162,31 +169,29 @@ private:
ScreenInfo& screen_info;
std::unique_ptr<GLShader::ProgramManager> shader_program_manager;
OGLVertexArray sw_vao;
OGLVertexArray hw_vao;
std::map<std::array<Tegra::Engines::Maxwell3D::Regs::VertexAttribute,
Tegra::Engines::Maxwell3D::Regs::NumVertexAttributes>,
OGLVertexArray>
vertex_array_cache;
std::array<SamplerInfo, GLShader::NumTextureSamplers> texture_samplers;
static constexpr size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024;
OGLStreamBuffer stream_buffer;
OGLBuffer uniform_buffer;
OGLBufferCache buffer_cache;
OGLFramebuffer framebuffer;
GLint uniform_buffer_alignment;
size_t CalculateVertexArraysSize() const;
std::pair<u8*, GLintptr> SetupVertexArrays(u8* array_ptr, GLintptr buffer_offset);
void SetupVertexArrays();
std::pair<u8*, GLintptr> SetupShaders(u8* buffer_ptr, GLintptr buffer_offset);
std::pair<u8*, GLintptr> AlignBuffer(u8* buffer_ptr, GLintptr buffer_offset, size_t alignment);
std::tuple<u8*, GLintptr, GLintptr> UploadMemory(u8* buffer_ptr, GLintptr buffer_offset,
Tegra::GPUVAddr gpu_addr, size_t size,
size_t alignment = 4);
void SetupShaders();
enum class AccelDraw { Disabled, Arrays, Indexed };
AccelDraw accelerate_draw = AccelDraw::Disabled;
using CachedPageMap = boost::icl::interval_map<u64, int>;
CachedPageMap cached_pages;
};
} // namespace OpenGL
@@ -33,11 +33,16 @@ struct FormatTuple {
bool compressed;
};
static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
auto& gpu{Core::System::GetInstance().GPU()};
const auto cpu_addr{gpu.MemoryManager().GpuToCpuAddress(gpu_addr)};
return cpu_addr ? *cpu_addr : 0;
}
/*static*/ SurfaceParams SurfaceParams::CreateForTexture(
const Tegra::Texture::FullTextureInfo& config) {
SurfaceParams params{};
params.addr = config.tic.Address();
params.addr = TryGetCpuAddr(config.tic.Address());
params.is_tiled = config.tic.IsTiled();
params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0,
params.pixel_format =
@@ -55,9 +60,8 @@ struct FormatTuple {
/*static*/ SurfaceParams SurfaceParams::CreateForFramebuffer(
const Tegra::Engines::Maxwell3D::Regs::RenderTargetConfig& config) {
SurfaceParams params{};
params.addr = config.Address();
params.addr = TryGetCpuAddr(config.Address());
params.is_tiled = true;
params.block_height = Tegra::Texture::TICEntry::DefaultBlockHeight;
params.pixel_format = PixelFormatFromRenderTargetFormat(config.format);
@@ -75,9 +79,8 @@ struct FormatTuple {
/*static*/ SurfaceParams SurfaceParams::CreateForDepthBuffer(u32 zeta_width, u32 zeta_height,
Tegra::GPUVAddr zeta_address,
Tegra::DepthFormat format) {
SurfaceParams params{};
params.addr = zeta_address;
params.addr = TryGetCpuAddr(zeta_address);
params.is_tiled = true;
params.block_height = Tegra::Texture::TICEntry::DefaultBlockHeight;
params.pixel_format = PixelFormatFromDepthFormat(format);
@@ -120,7 +123,11 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
true}, // DXN2UNORM
{GL_COMPRESSED_SIGNED_RG_RGTC2, GL_RG, GL_INT, ComponentType::SNorm, true}, // DXN2SNORM
{GL_COMPRESSED_RGBA_BPTC_UNORM_ARB, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
true}, // BC7U
true}, // BC7U
{GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB, GL_RGB, GL_UNSIGNED_INT_8_8_8_8,
ComponentType::UNorm, true}, // BC6H_UF16
{GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm,
true}, // BC6H_SF16
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4
{GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // G8R8U
{GL_RG8, GL_RG, GL_BYTE, ComponentType::SNorm, false}, // G8R8S
@@ -167,11 +174,6 @@ static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType
return format;
}
VAddr SurfaceParams::GetCpuAddr() const {
auto& gpu = Core::System::GetInstance().GPU();
return *gpu.MemoryManager().GpuToCpuAddress(addr);
}
static bool IsPixelFormatASTC(PixelFormat format) {
switch (format) {
case PixelFormat::ASTC_2D_4X4:
@@ -210,39 +212,36 @@ static bool IsFormatBCn(PixelFormat format) {
case PixelFormat::DXN2SNORM:
case PixelFormat::DXN2UNORM:
case PixelFormat::BC7U:
case PixelFormat::BC6H_UF16:
case PixelFormat::BC6H_SF16:
return true;
}
return false;
}
template <bool morton_to_gl, PixelFormat format>
void MortonCopy(u32 stride, u32 block_height, u32 height, std::vector<u8>& gl_buffer,
Tegra::GPUVAddr addr) {
void MortonCopy(u32 stride, u32 block_height, u32 height, std::vector<u8>& gl_buffer, VAddr addr) {
constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / CHAR_BIT;
constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format);
auto& gpu = Core::System::GetInstance().GPU();
if (morton_to_gl) {
// With the BCn formats (DXT and DXN), each 4x4 tile is swizzled instead of just individual
// pixel values.
const u32 tile_size{IsFormatBCn(format) ? 4U : 1U};
const std::vector<u8> data =
Tegra::Texture::UnswizzleTexture(*gpu.MemoryManager().GpuToCpuAddress(addr), tile_size,
bytes_per_pixel, stride, height, block_height);
const std::vector<u8> data = Tegra::Texture::UnswizzleTexture(
addr, tile_size, bytes_per_pixel, stride, height, block_height);
const size_t size_to_copy{std::min(gl_buffer.size(), data.size())};
gl_buffer.assign(data.begin(), data.begin() + size_to_copy);
} else {
// TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should
// check the configuration for this and perform more generic un/swizzle
LOG_WARNING(Render_OpenGL, "need to use correct swizzle/GOB parameters!");
VideoCore::MortonCopyPixels128(
stride, height, bytes_per_pixel, gl_bytes_per_pixel,
Memory::GetPointer(*gpu.MemoryManager().GpuToCpuAddress(addr)), gl_buffer.data(),
morton_to_gl);
VideoCore::MortonCopyPixels128(stride, height, bytes_per_pixel, gl_bytes_per_pixel,
Memory::GetPointer(addr), gl_buffer.data(), morton_to_gl);
}
}
static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, Tegra::GPUVAddr),
static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, VAddr),
SurfaceParams::MaxPixelFormat>
morton_to_gl_fns = {
// clang-format off
@@ -266,6 +265,8 @@ static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, Tegra::GPU
MortonCopy<true, PixelFormat::DXN2UNORM>,
MortonCopy<true, PixelFormat::DXN2SNORM>,
MortonCopy<true, PixelFormat::BC7U>,
MortonCopy<true, PixelFormat::BC6H_UF16>,
MortonCopy<true, PixelFormat::BC6H_SF16>,
MortonCopy<true, PixelFormat::ASTC_2D_4X4>,
MortonCopy<true, PixelFormat::G8R8U>,
MortonCopy<true, PixelFormat::G8R8S>,
@@ -297,7 +298,7 @@ static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, Tegra::GPU
// clang-format on
};
static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, Tegra::GPUVAddr),
static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, VAddr),
SurfaceParams::MaxPixelFormat>
gl_to_morton_fns = {
// clang-format off
@@ -314,8 +315,10 @@ static constexpr std::array<void (*)(u32, u32, u32, std::vector<u8>&, Tegra::GPU
MortonCopy<false, PixelFormat::RGBA16UI>,
MortonCopy<false, PixelFormat::R11FG11FB10F>,
MortonCopy<false, PixelFormat::RGBA32UI>,
// TODO(Subv): Swizzling DXT1/DXT23/DXT45/DXN1/DXN2/BC7U/ASTC_2D_4X4 formats is not
// supported
// TODO(Subv): Swizzling DXT1/DXT23/DXT45/DXN1/DXN2/BC7U/BC6H_UF16/BC6H_SF16/ASTC_2D_4X4
// formats are not supported
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
@@ -532,7 +535,7 @@ MICROPROFILE_DEFINE(OpenGL_SurfaceLoad, "OpenGL", "Surface Load", MP_RGB(128, 64
void CachedSurface::LoadGLBuffer() {
ASSERT(params.type != SurfaceType::Fill);
const u8* const texture_src_data = Memory::GetPointer(params.GetCpuAddr());
const u8* const texture_src_data = Memory::GetPointer(params.addr);
ASSERT(texture_src_data);
@@ -557,7 +560,7 @@ void CachedSurface::LoadGLBuffer() {
MICROPROFILE_DEFINE(OpenGL_SurfaceFlush, "OpenGL", "Surface Flush", MP_RGB(128, 192, 64));
void CachedSurface::FlushGLBuffer() {
u8* const dst_buffer = Memory::GetPointer(params.GetCpuAddr());
u8* const dst_buffer = Memory::GetPointer(params.addr);
ASSERT(dst_buffer);
ASSERT(gl_buffer.size() ==
@@ -687,7 +690,7 @@ SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces(bool usin
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
// TODO(bunnei): This is hard corded to use just the first render buffer
LOG_WARNING(Render_OpenGL, "hard-coded for render target 0!");
LOG_TRACE(Render_OpenGL, "hard-coded for render target 0!");
// get color and depth surfaces
SurfaceParams color_params{};
@@ -754,19 +757,10 @@ Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool pres
return {};
}
auto& gpu = Core::System::GetInstance().GPU();
// Don't try to create any entries in the cache if the address of the texture is invalid.
if (gpu.MemoryManager().GpuToCpuAddress(params.addr) == boost::none)
return {};
// Look up surface in the cache based on address
Surface surface{TryGet(params.addr)};
if (surface) {
if (Settings::values.use_accurate_framebuffers) {
// If use_accurate_framebuffers is enabled, always load from memory
FlushSurface(surface);
Unregister(surface);
} else if (surface->GetSurfaceParams().IsCompatibleSurface(params)) {
if (surface->GetSurfaceParams().IsCompatibleSurface(params)) {
// Use the cached surface as-is
return surface;
} else if (preserve_contents) {
@@ -782,15 +776,9 @@ Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool pres
}
}
// Try to get a previously reserved surface
surface = TryGetReservedSurface(params);
// No surface found - create a new one
if (!surface) {
surface = std::make_shared<CachedSurface>(params);
ReserveSurface(surface);
Register(surface);
}
// No cached surface found - get a new one
surface = GetUncachedSurface(params);
Register(surface);
// Only load surface from memory if we care about the contents
if (preserve_contents) {
@@ -800,13 +788,23 @@ Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool pres
return surface;
}
Surface RasterizerCacheOpenGL::GetUncachedSurface(const SurfaceParams& params) {
Surface surface{TryGetReservedSurface(params)};
if (!surface) {
// No reserved surface available, create a new one and reserve it
surface = std::make_shared<CachedSurface>(params);
ReserveSurface(surface);
}
return surface;
}
Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& surface,
const SurfaceParams& new_params) {
// Verify surface is compatible for blitting
const auto& params{surface->GetSurfaceParams()};
// Create a new surface with the new parameters, and blit the previous surface to it
Surface new_surface{std::make_shared<CachedSurface>(new_params)};
// Get a new surface with the new parameters, and blit the previous surface to it
Surface new_surface{GetUncachedSurface(new_params)};
// If format is unchanged, we can do a faster blit without reinterpreting pixel data
if (params.pixel_format == new_params.pixel_format) {
@@ -816,92 +814,73 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& surface,
return new_surface;
}
auto source_format = GetFormatTuple(params.pixel_format, params.component_type);
auto dest_format = GetFormatTuple(new_params.pixel_format, new_params.component_type);
// When using accurate framebuffers, always copy old data to new surface, regardless of format
if (Settings::values.use_accurate_framebuffers) {
auto source_format = GetFormatTuple(params.pixel_format, params.component_type);
auto dest_format = GetFormatTuple(new_params.pixel_format, new_params.component_type);
size_t buffer_size = std::max(params.SizeInBytes(), new_params.SizeInBytes());
size_t buffer_size = std::max(params.SizeInBytes(), new_params.SizeInBytes());
// Use a Pixel Buffer Object to download the previous texture and then upload it to the new one
// using the new format.
OGLBuffer pbo;
pbo.Create();
// Use a Pixel Buffer Object to download the previous texture and then upload it to the new
// one using the new format.
OGLBuffer pbo;
pbo.Create();
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo.handle);
glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB);
if (source_format.compressed) {
glGetCompressedTextureImage(surface->Texture().handle, 0,
static_cast<GLsizei>(params.SizeInBytes()), nullptr);
} else {
glGetTextureImage(surface->Texture().handle, 0, source_format.format, source_format.type,
static_cast<GLsizei>(params.SizeInBytes()), nullptr);
}
// If the new texture is bigger than the previous one, we need to fill in the rest with data
// from the CPU.
if (params.SizeInBytes() < new_params.SizeInBytes()) {
// Upload the rest of the memory.
if (new_params.is_tiled) {
// TODO(Subv): We might have to de-tile the subtexture and re-tile it with the rest of
// the data in this case. Games like Super Mario Odyssey seem to hit this case when
// drawing, it re-uses the memory of a previous texture as a bigger framebuffer but it
// doesn't clear it beforehand, the texture is already full of zeros.
LOG_CRITICAL(HW_GPU, "Trying to upload extra texture data from the CPU during "
"reinterpretation but the texture is tiled.");
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo.handle);
glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB);
if (source_format.compressed) {
glGetCompressedTextureImage(surface->Texture().handle, 0,
static_cast<GLsizei>(params.SizeInBytes()), nullptr);
} else {
glGetTextureImage(surface->Texture().handle, 0, source_format.format,
source_format.type, static_cast<GLsizei>(params.SizeInBytes()),
nullptr);
}
size_t remaining_size = new_params.SizeInBytes() - params.SizeInBytes();
auto address = Core::System::GetInstance().GPU().MemoryManager().GpuToCpuAddress(
new_params.addr + params.SizeInBytes());
std::vector<u8> data(remaining_size);
Memory::ReadBlock(*address, data.data(), data.size());
glBufferSubData(GL_PIXEL_PACK_BUFFER, params.SizeInBytes(), remaining_size, data.data());
// If the new texture is bigger than the previous one, we need to fill in the rest with data
// from the CPU.
if (params.SizeInBytes() < new_params.SizeInBytes()) {
// Upload the rest of the memory.
if (new_params.is_tiled) {
// TODO(Subv): We might have to de-tile the subtexture and re-tile it with the rest
// of the data in this case. Games like Super Mario Odyssey seem to hit this case
// when drawing, it re-uses the memory of a previous texture as a bigger framebuffer
// but it doesn't clear it beforehand, the texture is already full of zeros.
LOG_CRITICAL(HW_GPU, "Trying to upload extra texture data from the CPU during "
"reinterpretation but the texture is tiled.");
}
size_t remaining_size = new_params.SizeInBytes() - params.SizeInBytes();
std::vector<u8> data(remaining_size);
Memory::ReadBlock(new_params.addr + params.SizeInBytes(), data.data(), data.size());
glBufferSubData(GL_PIXEL_PACK_BUFFER, params.SizeInBytes(), remaining_size,
data.data());
}
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
const auto& dest_rect{new_params.GetRect()};
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo.handle);
if (dest_format.compressed) {
glCompressedTexSubImage2D(
GL_TEXTURE_2D, 0, 0, 0, static_cast<GLsizei>(dest_rect.GetWidth()),
static_cast<GLsizei>(dest_rect.GetHeight()), dest_format.format,
static_cast<GLsizei>(new_params.SizeInBytes()), nullptr);
} else {
glTextureSubImage2D(new_surface->Texture().handle, 0, 0, 0,
static_cast<GLsizei>(dest_rect.GetWidth()),
static_cast<GLsizei>(dest_rect.GetHeight()), dest_format.format,
dest_format.type, nullptr);
}
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
pbo.Release();
}
glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
const auto& dest_rect{new_params.GetRect()};
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo.handle);
if (dest_format.compressed) {
glCompressedTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
static_cast<GLsizei>(dest_rect.GetWidth()),
static_cast<GLsizei>(dest_rect.GetHeight()), dest_format.format,
static_cast<GLsizei>(new_params.SizeInBytes()), nullptr);
} else {
glTextureSubImage2D(new_surface->Texture().handle, 0, 0, 0,
static_cast<GLsizei>(dest_rect.GetWidth()),
static_cast<GLsizei>(dest_rect.GetHeight()), dest_format.format,
dest_format.type, nullptr);
}
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
pbo.Release();
return new_surface;
}
Surface RasterizerCacheOpenGL::TryFindFramebufferSurface(VAddr cpu_addr) const {
// Tries to find the GPU address of a framebuffer based on the CPU address. This is because
// final output framebuffers are specified by CPU address, but internally our GPU cache uses
// GPU addresses. We iterate through all cached framebuffers, and compare their starting CPU
// address to the one provided. This is obviously not great, and won't work if the
// framebuffer overlaps surfaces.
std::vector<Surface> surfaces;
for (const auto& surface : GetCache()) {
const auto& params = surface.second->GetSurfaceParams();
const VAddr surface_cpu_addr = params.GetCpuAddr();
if (cpu_addr >= surface_cpu_addr && cpu_addr < (surface_cpu_addr + params.size_in_bytes)) {
ASSERT_MSG(cpu_addr == surface_cpu_addr, "overlapping surfaces are unsupported");
surfaces.push_back(surface.second);
}
}
if (surfaces.empty()) {
return {};
}
ASSERT_MSG(surfaces.size() == 1, ">1 surface is unsupported");
return surfaces[0];
Surface RasterizerCacheOpenGL::TryFindFramebufferSurface(VAddr addr) const {
return TryGet(addr);
}
void RasterizerCacheOpenGL::ReserveSurface(const Surface& surface) {
@@ -913,7 +892,6 @@ Surface RasterizerCacheOpenGL::TryGetReservedSurface(const SurfaceParams& params
const auto& surface_reserve_key{SurfaceReserveKey::Create(params)};
auto search{surface_reserve.find(surface_reserve_key)};
if (search != surface_reserve.end()) {
Register(search->second);
return search->second;
}
return {};
@@ -45,42 +45,44 @@ struct SurfaceParams {
DXN2UNORM = 17,
DXN2SNORM = 18,
BC7U = 19,
ASTC_2D_4X4 = 20,
G8R8U = 21,
G8R8S = 22,
BGRA8 = 23,
RGBA32F = 24,
RG32F = 25,
R32F = 26,
R16F = 27,
R16U = 28,
R16S = 29,
R16UI = 30,
R16I = 31,
RG16 = 32,
RG16F = 33,
RG16UI = 34,
RG16I = 35,
RG16S = 36,
RGB32F = 37,
SRGBA8 = 38,
RG8U = 39,
RG8S = 40,
RG32UI = 41,
R32UI = 42,
BC6H_UF16 = 20,
BC6H_SF16 = 21,
ASTC_2D_4X4 = 22,
G8R8U = 23,
G8R8S = 24,
BGRA8 = 25,
RGBA32F = 26,
RG32F = 27,
R32F = 28,
R16F = 29,
R16U = 30,
R16S = 31,
R16UI = 32,
R16I = 33,
RG16 = 34,
RG16F = 35,
RG16UI = 36,
RG16I = 37,
RG16S = 38,
RGB32F = 39,
SRGBA8 = 40,
RG8U = 41,
RG8S = 42,
RG32UI = 43,
R32UI = 44,
MaxColorFormat,
// Depth formats
Z32F = 43,
Z16 = 44,
Z32F = 45,
Z16 = 46,
MaxDepthFormat,
// DepthStencil formats
Z24S8 = 45,
S8Z24 = 46,
Z32FS8 = 47,
Z24S8 = 47,
S8Z24 = 48,
Z32FS8 = 49,
MaxDepthStencilFormat,
@@ -138,6 +140,8 @@ struct SurfaceParams {
4, // DXN2UNORM
4, // DXN2SNORM
4, // BC7U
4, // BC6H_UF16
4, // BC6H_SF16
4, // ASTC_2D_4X4
1, // G8R8U
1, // G8R8S
@@ -197,6 +201,8 @@ struct SurfaceParams {
128, // DXN2UNORM
128, // DXN2SNORM
128, // BC7U
128, // BC6H_UF16
128, // BC6H_SF16
32, // ASTC_2D_4X4
16, // G8R8U
16, // G8R8S
@@ -482,6 +488,10 @@ struct SurfaceParams {
UNREACHABLE();
case Tegra::Texture::TextureFormat::BC7U:
return PixelFormat::BC7U;
case Tegra::Texture::TextureFormat::BC6H_UF16:
return PixelFormat::BC6H_UF16;
case Tegra::Texture::TextureFormat::BC6H_SF16:
return PixelFormat::BC6H_SF16;
case Tegra::Texture::TextureFormat::ASTC_2D_4X4:
return PixelFormat::ASTC_2D_4X4;
case Tegra::Texture::TextureFormat::R16_G16:
@@ -628,9 +638,6 @@ struct SurfaceParams {
GetFormatBpp(pixel_format) / CHAR_BIT;
}
/// Returns the CPU virtual address for this surface
VAddr GetCpuAddr() const;
/// Creates SurfaceParams from a texture configuration
static SurfaceParams CreateForTexture(const Tegra::Texture::FullTextureInfo& config);
@@ -643,25 +650,13 @@ struct SurfaceParams {
Tegra::GPUVAddr zeta_address,
Tegra::DepthFormat format);
bool operator==(const SurfaceParams& other) const {
return std::tie(addr, is_tiled, block_height, pixel_format, component_type, type, width,
height, unaligned_height, size_in_bytes) ==
std::tie(other.addr, other.is_tiled, other.block_height, other.pixel_format,
other.component_type, other.type, other.width, other.height,
other.unaligned_height, other.size_in_bytes);
}
bool operator!=(const SurfaceParams& other) const {
return !operator==(other);
}
/// Checks if surfaces are compatible for caching
bool IsCompatibleSurface(const SurfaceParams& other) const {
return std::tie(pixel_format, type, cache_width, cache_height) ==
std::tie(other.pixel_format, other.type, other.cache_width, other.cache_height);
}
Tegra::GPUVAddr addr;
VAddr addr;
bool is_tiled;
u32 block_height;
PixelFormat pixel_format;
@@ -702,7 +697,7 @@ class CachedSurface final {
public:
CachedSurface(const SurfaceParams& params);
Tegra::GPUVAddr GetAddr() const {
VAddr GetAddr() const {
return params.addr;
}
@@ -753,13 +748,16 @@ public:
/// Flushes the surface to Switch memory
void FlushSurface(const Surface& surface);
/// Tries to find a framebuffer GPU address based on the provided CPU address
Surface TryFindFramebufferSurface(VAddr cpu_addr) const;
/// Tries to find a framebuffer using on the provided CPU address
Surface TryFindFramebufferSurface(VAddr addr) const;
private:
void LoadSurface(const Surface& surface);
Surface GetSurface(const SurfaceParams& params, bool preserve_contents = true);
/// Gets an uncached surface, creating it if need be
Surface GetUncachedSurface(const SurfaceParams& params);
/// Recreates a surface with new parameters
Surface RecreateSurface(const Surface& surface, const SurfaceParams& new_params);
@@ -12,21 +12,17 @@
namespace OpenGL {
/// Gets the address for the specified shader stage program
static Tegra::GPUVAddr GetShaderAddress(Maxwell::ShaderProgram program) {
static VAddr GetShaderAddress(Maxwell::ShaderProgram program) {
auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
auto& shader_config = gpu.regs.shader_config[static_cast<size_t>(program)];
return gpu.regs.code_address.CodeAddress() + shader_config.offset;
return *gpu.memory_manager.GpuToCpuAddress(gpu.regs.code_address.CodeAddress() +
shader_config.offset);
}
/// Gets the shader program code from memory for the specified address
static GLShader::ProgramCode GetShaderCode(Tegra::GPUVAddr addr) {
auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
static GLShader::ProgramCode GetShaderCode(VAddr addr) {
GLShader::ProgramCode program_code(GLShader::MAX_PROGRAM_CODE_LENGTH);
const boost::optional<VAddr> cpu_address{gpu.memory_manager.GpuToCpuAddress(addr)};
Memory::ReadBlock(*cpu_address, program_code.data(), program_code.size() * sizeof(u64));
Memory::ReadBlock(addr, program_code.data(), program_code.size() * sizeof(u64));
return program_code;
}
@@ -55,7 +51,7 @@ static void SetShaderUniformBlockBindings(GLuint shader) {
sizeof(GLShader::MaxwellUniformData));
}
CachedShader::CachedShader(Tegra::GPUVAddr addr, Maxwell::ShaderProgram program_type)
CachedShader::CachedShader(VAddr addr, Maxwell::ShaderProgram program_type)
: addr{addr}, program_type{program_type}, setup{GetShaderCode(addr)} {
GLShader::ProgramResult program_result;
@@ -89,23 +85,23 @@ CachedShader::CachedShader(Tegra::GPUVAddr addr, Maxwell::ShaderProgram program_
SetShaderUniformBlockBindings(program.handle);
}
GLuint CachedShader::GetProgramResourceIndex(const std::string& name) {
auto search{resource_cache.find(name)};
GLuint CachedShader::GetProgramResourceIndex(const GLShader::ConstBufferEntry& buffer) {
auto search{resource_cache.find(buffer.GetHash())};
if (search == resource_cache.end()) {
const GLuint index{
glGetProgramResourceIndex(program.handle, GL_UNIFORM_BLOCK, name.c_str())};
resource_cache[name] = index;
glGetProgramResourceIndex(program.handle, GL_UNIFORM_BLOCK, buffer.GetName().c_str())};
resource_cache[buffer.GetHash()] = index;
return index;
}
return search->second;
}
GLint CachedShader::GetUniformLocation(const std::string& name) {
auto search{uniform_cache.find(name)};
GLint CachedShader::GetUniformLocation(const GLShader::SamplerEntry& sampler) {
auto search{uniform_cache.find(sampler.GetHash())};
if (search == uniform_cache.end()) {
const GLint index{glGetUniformLocation(program.handle, name.c_str())};
uniform_cache[name] = index;
const GLint index{glGetUniformLocation(program.handle, sampler.GetName().c_str())};
uniform_cache[sampler.GetHash()] = index;
return index;
}
@@ -113,7 +109,7 @@ GLint CachedShader::GetUniformLocation(const std::string& name) {
}
Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
const Tegra::GPUVAddr program_addr{GetShaderAddress(program)};
const VAddr program_addr{GetShaderAddress(program)};
// Look up shader in the cache based on address
Shader shader{TryGet(program_addr)};

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