diff options
250 files changed, 5055 insertions, 1723 deletions
diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..ab861a396 --- /dev/null +++ b/.gitattributes | |||
| @@ -0,0 +1,4 @@ | |||
| 1 | dist/languages/* linguist-vendored | ||
| 2 | dist/qt_themes/* linguist-vendored | ||
| 3 | externals/* linguist-vendored | ||
| 4 | *.h linguist-language=cpp | ||
diff --git a/.travis.yml b/.travis.yml index dee34a8e3..b0fbe3c5f 100644 --- a/.travis.yml +++ b/.travis.yml | |||
| @@ -24,11 +24,24 @@ matrix: | |||
| 24 | - os: osx | 24 | - os: osx |
| 25 | env: NAME="macos build" | 25 | env: NAME="macos build" |
| 26 | sudo: false | 26 | sudo: false |
| 27 | osx_image: xcode9.3 | 27 | osx_image: xcode10 |
| 28 | install: "./.travis/macos/deps.sh" | 28 | install: "./.travis/macos/deps.sh" |
| 29 | script: "./.travis/macos/build.sh" | 29 | script: "./.travis/macos/build.sh" |
| 30 | after_success: "./.travis/macos/upload.sh" | 30 | after_success: "./.travis/macos/upload.sh" |
| 31 | cache: ccache | 31 | cache: ccache |
| 32 | - os: linux | ||
| 33 | env: NAME="MinGW build" | ||
| 34 | sudo: required | ||
| 35 | dist: trusty | ||
| 36 | services: docker | ||
| 37 | addons: | ||
| 38 | apt: | ||
| 39 | packages: | ||
| 40 | - p7zip-full | ||
| 41 | install: "./.travis/linux-mingw/deps.sh" | ||
| 42 | script: "./.travis/linux-mingw/build.sh" | ||
| 43 | after_success: "./.travis/linux-mingw/upload.sh" | ||
| 44 | cache: ccache | ||
| 32 | 45 | ||
| 33 | deploy: | 46 | deploy: |
| 34 | provider: releases | 47 | provider: releases |
diff --git a/.travis/common/post-upload.sh b/.travis/common/post-upload.sh index 90deaaec8..28735a9cf 100755 --- a/.travis/common/post-upload.sh +++ b/.travis/common/post-upload.sh | |||
| @@ -11,6 +11,9 @@ if [ -z $TRAVIS_TAG ]; then | |||
| 11 | RELEASE_NAME=head | 11 | RELEASE_NAME=head |
| 12 | else | 12 | else |
| 13 | RELEASE_NAME=$(echo $TRAVIS_TAG | cut -d- -f1) | 13 | RELEASE_NAME=$(echo $TRAVIS_TAG | cut -d- -f1) |
| 14 | if [ "$NAME" = "MinGW build" ]; then | ||
| 15 | RELEASE_NAME="${RELEASE_NAME}-mingw" | ||
| 16 | fi | ||
| 14 | fi | 17 | fi |
| 15 | 18 | ||
| 16 | mv "$REV_NAME" $RELEASE_NAME | 19 | mv "$REV_NAME" $RELEASE_NAME |
diff --git a/.travis/linux-mingw/build.sh b/.travis/linux-mingw/build.sh new file mode 100755 index 000000000..be03cc0f3 --- /dev/null +++ b/.travis/linux-mingw/build.sh | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | #!/bin/bash -ex | ||
| 2 | mkdir "$HOME/.ccache" || true | ||
| 3 | docker run --env-file .travis/common/travis-ci.env -v $(pwd):/yuzu -v "$HOME/.ccache":/root/.ccache ubuntu:18.04 /bin/bash -ex /yuzu/.travis/linux-mingw/docker.sh | ||
diff --git a/.travis/linux-mingw/deps.sh b/.travis/linux-mingw/deps.sh new file mode 100755 index 000000000..540bb934a --- /dev/null +++ b/.travis/linux-mingw/deps.sh | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | #!/bin/sh -ex | ||
| 2 | |||
| 3 | docker pull ubuntu:18.04 | ||
diff --git a/.travis/linux-mingw/docker.sh b/.travis/linux-mingw/docker.sh new file mode 100755 index 000000000..d15c3f6e8 --- /dev/null +++ b/.travis/linux-mingw/docker.sh | |||
| @@ -0,0 +1,59 @@ | |||
| 1 | #!/bin/bash -ex | ||
| 2 | |||
| 3 | cd /yuzu | ||
| 4 | MINGW_PACKAGES="sdl2-mingw-w64 qt5base-mingw-w64 qt5tools-mingw-w64 libsamplerate-mingw-w64 qt5multimedia-mingw-w64" | ||
| 5 | apt-get update | ||
| 6 | apt-get install -y gpg wget git python3-pip python ccache g++-mingw-w64-x86-64 gcc-mingw-w64-x86-64 mingw-w64-tools cmake | ||
| 7 | echo 'deb http://ppa.launchpad.net/tobydox/mingw-w64/ubuntu bionic main ' > /etc/apt/sources.list.d/extras.list | ||
| 8 | apt-key adv --keyserver keyserver.ubuntu.com --recv '72931B477E22FEFD47F8DECE02FE5F12ADDE29B2' | ||
| 9 | apt-get update | ||
| 10 | apt-get install -y ${MINGW_PACKAGES} | ||
| 11 | |||
| 12 | # fix a problem in current MinGW headers | ||
| 13 | wget -q https://raw.githubusercontent.com/Alexpux/mingw-w64/d0d7f784833bbb0b2d279310ddc6afb52fe47a46/mingw-w64-headers/crt/errno.h -O /usr/x86_64-w64-mingw32/include/errno.h | ||
| 14 | # override Travis CI unreasonable ccache size | ||
| 15 | echo 'max_size = 3.0G' > "$HOME/.ccache/ccache.conf" | ||
| 16 | |||
| 17 | # Dirty hack to trick unicorn makefile into believing we are in a MINGW system | ||
| 18 | mv /bin/uname /bin/uname1 && echo -e '#!/bin/sh\necho MINGW64' >> /bin/uname | ||
| 19 | chmod +x /bin/uname | ||
| 20 | |||
| 21 | # Dirty hack to trick unicorn makefile into believing we have cmd | ||
| 22 | echo '' >> /bin/cmd | ||
| 23 | chmod +x /bin/cmd | ||
| 24 | |||
| 25 | mkdir build && cd build | ||
| 26 | cmake .. -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_UNICORN=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release | ||
| 27 | make -j4 | ||
| 28 | |||
| 29 | # Clean up the dirty hacks | ||
| 30 | rm /bin/uname && mv /bin/uname1 /bin/uname | ||
| 31 | rm /bin/cmd | ||
| 32 | |||
| 33 | ccache -s | ||
| 34 | |||
| 35 | echo "Tests skipped" | ||
| 36 | #ctest -VV -C Release | ||
| 37 | |||
| 38 | echo 'Prepare binaries...' | ||
| 39 | cd .. | ||
| 40 | mkdir package | ||
| 41 | |||
| 42 | QT_PLATFORM_DLL_PATH='/usr/x86_64-w64-mingw32/lib/qt5/plugins/platforms/' | ||
| 43 | find build/ -name "yuzu*.exe" -exec cp {} 'package' \; | ||
| 44 | |||
| 45 | # copy Qt plugins | ||
| 46 | mkdir package/platforms | ||
| 47 | cp "${QT_PLATFORM_DLL_PATH}/qwindows.dll" package/platforms/ | ||
| 48 | cp -rv "${QT_PLATFORM_DLL_PATH}/../mediaservice/" package/ | ||
| 49 | cp -rv "${QT_PLATFORM_DLL_PATH}/../imageformats/" package/ | ||
| 50 | rm -f package/mediaservice/*d.dll | ||
| 51 | |||
| 52 | for i in package/*.exe; do | ||
| 53 | # we need to process pdb here, however, cv2pdb | ||
| 54 | # does not work here, so we just simply strip all the debug symbols | ||
| 55 | x86_64-w64-mingw32-strip "${i}" | ||
| 56 | done | ||
| 57 | |||
| 58 | pip3 install pefile | ||
| 59 | python3 .travis/linux-mingw/scan_dll.py package/*.exe "package/" | ||
diff --git a/.travis/linux-mingw/scan_dll.py b/.travis/linux-mingw/scan_dll.py new file mode 100644 index 000000000..163183f2e --- /dev/null +++ b/.travis/linux-mingw/scan_dll.py | |||
| @@ -0,0 +1,106 @@ | |||
| 1 | import pefile | ||
| 2 | import sys | ||
| 3 | import re | ||
| 4 | import os | ||
| 5 | import queue | ||
| 6 | import shutil | ||
| 7 | |||
| 8 | # constant definitions | ||
| 9 | KNOWN_SYS_DLLS = ['WINMM.DLL', 'MSVCRT.DLL', 'VERSION.DLL', 'MPR.DLL', | ||
| 10 | 'DWMAPI.DLL', 'UXTHEME.DLL', 'DNSAPI.DLL', 'IPHLPAPI.DLL'] | ||
| 11 | # below is for Ubuntu 18.04 with specified PPA enabled, if you are using | ||
| 12 | # other distro or different repositories, change the following accordingly | ||
| 13 | DLL_PATH = [ | ||
| 14 | '/usr/x86_64-w64-mingw32/bin/', | ||
| 15 | '/usr/x86_64-w64-mingw32/lib/', | ||
| 16 | '/usr/lib/gcc/x86_64-w64-mingw32/7.3-posix/' | ||
| 17 | ] | ||
| 18 | |||
| 19 | missing = [] | ||
| 20 | |||
| 21 | |||
| 22 | def parse_imports(file_name): | ||
| 23 | results = [] | ||
| 24 | pe = pefile.PE(file_name, fast_load=True) | ||
| 25 | pe.parse_data_directories() | ||
| 26 | |||
| 27 | for entry in pe.DIRECTORY_ENTRY_IMPORT: | ||
| 28 | current = entry.dll.decode() | ||
| 29 | current_u = current.upper() # b/c Windows is often case insensitive | ||
| 30 | # here we filter out system dlls | ||
| 31 | # dll w/ names like *32.dll are likely to be system dlls | ||
| 32 | if current_u.upper() not in KNOWN_SYS_DLLS and not re.match(string=current_u, pattern=r'.*32\.DLL'): | ||
| 33 | results.append(current) | ||
| 34 | |||
| 35 | return results | ||
| 36 | |||
| 37 | |||
| 38 | def parse_imports_recursive(file_name, path_list=[]): | ||
| 39 | q = queue.Queue() # create a FIFO queue | ||
| 40 | # file_name can be a string or a list for the convience | ||
| 41 | if isinstance(file_name, str): | ||
| 42 | q.put(file_name) | ||
| 43 | elif isinstance(file_name, list): | ||
| 44 | for i in file_name: | ||
| 45 | q.put(i) | ||
| 46 | full_list = [] | ||
| 47 | while q.qsize(): | ||
| 48 | current = q.get_nowait() | ||
| 49 | print('> %s' % current) | ||
| 50 | deps = parse_imports(current) | ||
| 51 | # if this dll does not have any import, ignore it | ||
| 52 | if not deps: | ||
| 53 | continue | ||
| 54 | for dep in deps: | ||
| 55 | # the dependency already included in the list, skip | ||
| 56 | if dep in full_list: | ||
| 57 | continue | ||
| 58 | # find the requested dll in the provided paths | ||
| 59 | full_path = find_dll(dep) | ||
| 60 | if not full_path: | ||
| 61 | missing.append(dep) | ||
| 62 | continue | ||
| 63 | full_list.append(dep) | ||
| 64 | q.put(full_path) | ||
| 65 | path_list.append(full_path) | ||
| 66 | return full_list | ||
| 67 | |||
| 68 | |||
| 69 | def find_dll(name): | ||
| 70 | for path in DLL_PATH: | ||
| 71 | for root, _, files in os.walk(path): | ||
| 72 | for f in files: | ||
| 73 | if name.lower() == f.lower(): | ||
| 74 | return os.path.join(root, f) | ||
| 75 | |||
| 76 | |||
| 77 | def deploy(name, dst, dry_run=False): | ||
| 78 | dlls_path = [] | ||
| 79 | parse_imports_recursive(name, dlls_path) | ||
| 80 | for dll_entry in dlls_path: | ||
| 81 | if not dry_run: | ||
| 82 | shutil.copy(dll_entry, dst) | ||
| 83 | else: | ||
| 84 | print('[Dry-Run] Copy %s to %s' % (dll_entry, dst)) | ||
| 85 | print('Deploy completed.') | ||
| 86 | return dlls_path | ||
| 87 | |||
| 88 | |||
| 89 | def main(): | ||
| 90 | if len(sys.argv) < 3: | ||
| 91 | print('Usage: %s [files to examine ...] [target deploy directory]') | ||
| 92 | return 1 | ||
| 93 | to_deploy = sys.argv[1:-1] | ||
| 94 | tgt_dir = sys.argv[-1] | ||
| 95 | if not os.path.isdir(tgt_dir): | ||
| 96 | print('%s is not a directory.' % tgt_dir) | ||
| 97 | return 1 | ||
| 98 | print('Scanning dependencies...') | ||
| 99 | deploy(to_deploy, tgt_dir) | ||
| 100 | if missing: | ||
| 101 | print('Following DLLs are not found: %s' % ('\n'.join(missing))) | ||
| 102 | return 0 | ||
| 103 | |||
| 104 | |||
| 105 | if __name__ == '__main__': | ||
| 106 | main() | ||
diff --git a/.travis/linux-mingw/upload.sh b/.travis/linux-mingw/upload.sh new file mode 100755 index 000000000..66e896bc4 --- /dev/null +++ b/.travis/linux-mingw/upload.sh | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | #!/bin/bash -ex | ||
| 2 | |||
| 3 | . .travis/common/pre-upload.sh | ||
| 4 | |||
| 5 | REV_NAME="yuzu-windows-mingw-${GITDATE}-${GITREV}" | ||
| 6 | ARCHIVE_NAME="${REV_NAME}.tar.gz" | ||
| 7 | COMPRESSION_FLAGS="-czvf" | ||
| 8 | |||
| 9 | mkdir "$REV_NAME" | ||
| 10 | # get around the permission issues | ||
| 11 | cp -r package/* "$REV_NAME" | ||
| 12 | |||
| 13 | . .travis/common/post-upload.sh | ||
diff --git a/.travis/linux/docker.sh b/.travis/linux/docker.sh index 459d6bc75..892d2480a 100755 --- a/.travis/linux/docker.sh +++ b/.travis/linux/docker.sh | |||
| @@ -6,7 +6,9 @@ apt-get install --no-install-recommends -y build-essential git libqt5opengl5-dev | |||
| 6 | cd /yuzu | 6 | cd /yuzu |
| 7 | 7 | ||
| 8 | mkdir build && cd build | 8 | mkdir build && cd build |
| 9 | 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 | 9 | cmake .. -DYUZU_USE_BUNDLED_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -G Ninja |
| 10 | ninja | 10 | ninja |
| 11 | 11 | ||
| 12 | ccache -s | ||
| 13 | |||
| 12 | ctest -VV -C Release | 14 | ctest -VV -C Release |
diff --git a/.travis/macos/build.sh b/.travis/macos/build.sh index b76a153be..e68dc1400 100755 --- a/.travis/macos/build.sh +++ b/.travis/macos/build.sh | |||
| @@ -2,14 +2,16 @@ | |||
| 2 | 2 | ||
| 3 | set -o pipefail | 3 | set -o pipefail |
| 4 | 4 | ||
| 5 | export MACOSX_DEPLOYMENT_TARGET=10.12 | 5 | export MACOSX_DEPLOYMENT_TARGET=10.13 |
| 6 | export Qt5_DIR=$(brew --prefix)/opt/qt5 | 6 | export Qt5_DIR=$(brew --prefix)/opt/qt5 |
| 7 | export UNICORNDIR=$(pwd)/externals/unicorn | 7 | export UNICORNDIR=$(pwd)/externals/unicorn |
| 8 | export PATH="/usr/local/opt/ccache/libexec:$PATH" | 8 | export PATH="/usr/local/opt/ccache/libexec:$PATH" |
| 9 | 9 | ||
| 10 | mkdir build && cd build | 10 | mkdir build && cd build |
| 11 | cmake --version | 11 | cmake --version |
| 12 | cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON | 12 | cmake .. -DYUZU_USE_BUNDLED_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON |
| 13 | make -j4 | 13 | make -j4 |
| 14 | 14 | ||
| 15 | ccache -s | ||
| 16 | |||
| 15 | ctest -VV -C Release | 17 | ctest -VV -C Release |
diff --git a/CMakeLists.txt b/CMakeLists.txt index 500d099fc..cd990188e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
| @@ -123,8 +123,6 @@ else() | |||
| 123 | # Avoid windows.h from including some usually unused libs like winsocks.h, since this might cause some redefinition errors. | 123 | # Avoid windows.h from including some usually unused libs like winsocks.h, since this might cause some redefinition errors. |
| 124 | add_definitions(/DWIN32_LEAN_AND_MEAN) | 124 | add_definitions(/DWIN32_LEAN_AND_MEAN) |
| 125 | 125 | ||
| 126 | # set up output paths for executable binaries (.exe-files, and .dll-files on DLL-capable platforms) | ||
| 127 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) | ||
| 128 | set(CMAKE_CONFIGURATION_TYPES Debug Release CACHE STRING "" FORCE) | 126 | set(CMAKE_CONFIGURATION_TYPES Debug Release CACHE STRING "" FORCE) |
| 129 | 127 | ||
| 130 | # Tweak optimization settings | 128 | # Tweak optimization settings |
| @@ -269,10 +267,18 @@ if (YUZU_USE_BUNDLED_UNICORN) | |||
| 269 | 267 | ||
| 270 | find_package(PythonInterp 2.7 REQUIRED) | 268 | find_package(PythonInterp 2.7 REQUIRED) |
| 271 | 269 | ||
| 272 | add_custom_command(OUTPUT ${LIBUNICORN_LIBRARY} | 270 | if (MINGW) |
| 273 | COMMAND ${CMAKE_COMMAND} -E env UNICORN_ARCHS="aarch64" PYTHON="${PYTHON_EXECUTABLE}" /bin/sh make.sh macos-universal-no | 271 | add_custom_command(OUTPUT ${LIBUNICORN_LIBRARY} |
| 274 | WORKING_DIRECTORY ${UNICORN_PREFIX} | 272 | COMMAND ${CMAKE_COMMAND} -E env UNICORN_ARCHS="aarch64" PYTHON="${PYTHON_EXECUTABLE}" /bin/sh make.sh cross-win64 |
| 275 | ) | 273 | WORKING_DIRECTORY ${UNICORN_PREFIX} |
| 274 | ) | ||
| 275 | else() | ||
| 276 | add_custom_command(OUTPUT ${LIBUNICORN_LIBRARY} | ||
| 277 | COMMAND ${CMAKE_COMMAND} -E env UNICORN_ARCHS="aarch64" PYTHON="${PYTHON_EXECUTABLE}" /bin/sh make.sh macos-universal-no | ||
| 278 | WORKING_DIRECTORY ${UNICORN_PREFIX} | ||
| 279 | ) | ||
| 280 | endif() | ||
| 281 | |||
| 276 | # ALL makes this custom target build every time | 282 | # ALL makes this custom target build every time |
| 277 | # but it won't actually build if LIBUNICORN_LIBRARY is up to date | 283 | # but it won't actually build if LIBUNICORN_LIBRARY is up to date |
| 278 | add_custom_target(unicorn-build ALL | 284 | add_custom_target(unicorn-build ALL |
| @@ -286,6 +292,7 @@ endif() | |||
| 286 | 292 | ||
| 287 | if (UNICORN_FOUND) | 293 | if (UNICORN_FOUND) |
| 288 | add_library(unicorn INTERFACE) | 294 | add_library(unicorn INTERFACE) |
| 295 | add_dependencies(unicorn unicorn-build) | ||
| 289 | target_link_libraries(unicorn INTERFACE "${LIBUNICORN_LIBRARY}") | 296 | target_link_libraries(unicorn INTERFACE "${LIBUNICORN_LIBRARY}") |
| 290 | target_include_directories(unicorn INTERFACE "${LIBUNICORN_INCLUDE_DIR}") | 297 | target_include_directories(unicorn INTERFACE "${LIBUNICORN_INCLUDE_DIR}") |
| 291 | else() | 298 | else() |
| @@ -431,8 +438,12 @@ enable_testing() | |||
| 431 | add_subdirectory(externals) | 438 | add_subdirectory(externals) |
| 432 | add_subdirectory(src) | 439 | add_subdirectory(src) |
| 433 | 440 | ||
| 434 | # Set yuzu project as default StartUp Project in Visual Studio | 441 | # Set yuzu project or yuzu-cmd project as default StartUp Project in Visual Studio depending on whether QT is enabled or not |
| 435 | set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT yuzu) | 442 | if(ENABLE_QT) |
| 443 | set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT yuzu) | ||
| 444 | else() | ||
| 445 | set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT yuzu-cmd) | ||
| 446 | endif() | ||
| 436 | 447 | ||
| 437 | 448 | ||
| 438 | # Installation instructions | 449 | # Installation instructions |
diff --git a/CMakeModules/MinGWCross.cmake b/CMakeModules/MinGWCross.cmake new file mode 100644 index 000000000..29ecd1ac4 --- /dev/null +++ b/CMakeModules/MinGWCross.cmake | |||
| @@ -0,0 +1,54 @@ | |||
| 1 | set(MINGW_PREFIX /usr/x86_64-w64-mingw32/) | ||
| 2 | set(CMAKE_SYSTEM_NAME Windows) | ||
| 3 | set(CMAKE_SYSTEM_PROCESSOR x86_64) | ||
| 4 | # Actually a hack, w/o this will cause some strange errors | ||
| 5 | set(CMAKE_HOST_WIN32 TRUE) | ||
| 6 | |||
| 7 | |||
| 8 | set(CMAKE_FIND_ROOT_PATH ${MINGW_PREFIX}) | ||
| 9 | set(SDL2_PATH ${MINGW_PREFIX}) | ||
| 10 | set(MINGW_TOOL_PREFIX ${CMAKE_SYSTEM_PROCESSOR}-w64-mingw32-) | ||
| 11 | |||
| 12 | # Specify the cross compiler | ||
| 13 | set(CMAKE_C_COMPILER ${MINGW_TOOL_PREFIX}gcc-posix) | ||
| 14 | set(CMAKE_CXX_COMPILER ${MINGW_TOOL_PREFIX}g++-posix) | ||
| 15 | set(CMAKE_RC_COMPILER ${MINGW_TOOL_PREFIX}windres) | ||
| 16 | |||
| 17 | # Mingw tools | ||
| 18 | set(STRIP ${MINGW_TOOL_PREFIX}strip) | ||
| 19 | set(WINDRES ${MINGW_TOOL_PREFIX}windres) | ||
| 20 | set(ENV{PKG_CONFIG} ${MINGW_TOOL_PREFIX}pkg-config) | ||
| 21 | |||
| 22 | # ccache wrapper | ||
| 23 | option(USE_CCACHE "Use ccache for compilation" OFF) | ||
| 24 | if(USE_CCACHE) | ||
| 25 | find_program(CCACHE ccache) | ||
| 26 | if(CCACHE) | ||
| 27 | message(STATUS "Using ccache found in PATH") | ||
| 28 | set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE}) | ||
| 29 | set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ${CCACHE}) | ||
| 30 | else(CCACHE) | ||
| 31 | message(WARNING "USE_CCACHE enabled, but no ccache found") | ||
| 32 | endif(CCACHE) | ||
| 33 | endif(USE_CCACHE) | ||
| 34 | |||
| 35 | # Search for programs in the build host directories | ||
| 36 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) | ||
| 37 | |||
| 38 | |||
| 39 | # Echo modified cmake vars to screen for debugging purposes | ||
| 40 | if(NOT DEFINED ENV{MINGW_DEBUG_INFO}) | ||
| 41 | message("") | ||
| 42 | message("Custom cmake vars: (blank = system default)") | ||
| 43 | message("-----------------------------------------") | ||
| 44 | message("* CMAKE_C_COMPILER : ${CMAKE_C_COMPILER}") | ||
| 45 | message("* CMAKE_CXX_COMPILER : ${CMAKE_CXX_COMPILER}") | ||
| 46 | message("* CMAKE_RC_COMPILER : ${CMAKE_RC_COMPILER}") | ||
| 47 | message("* WINDRES : ${WINDRES}") | ||
| 48 | message("* ENV{PKG_CONFIG} : $ENV{PKG_CONFIG}") | ||
| 49 | message("* STRIP : ${STRIP}") | ||
| 50 | message("* USE_CCACHE : ${USE_CCACHE}") | ||
| 51 | message("") | ||
| 52 | # So that the debug info only appears once | ||
| 53 | set(ENV{MINGW_DEBUG_INFO} SHOWN) | ||
| 54 | endif() | ||
diff --git a/externals/dynarmic b/externals/dynarmic | |||
| Subproject 959446573f3adfcba173ef4b0011a4a280f18eb | Subproject 171d11659d760a4d4674d3a90698fe31ea407e2 | ||
diff --git a/src/audio_core/algorithm/filter.cpp b/src/audio_core/algorithm/filter.cpp index 9fcd0614d..f65bf64f7 100644 --- a/src/audio_core/algorithm/filter.cpp +++ b/src/audio_core/algorithm/filter.cpp | |||
| @@ -35,12 +35,12 @@ Filter::Filter(double a0, double a1, double a2, double b0, double b1, double b2) | |||
| 35 | : a1(a1 / a0), a2(a2 / a0), b0(b0 / a0), b1(b1 / a0), b2(b2 / a0) {} | 35 | : a1(a1 / a0), a2(a2 / a0), b0(b0 / a0), b1(b1 / a0), b2(b2 / a0) {} |
| 36 | 36 | ||
| 37 | void Filter::Process(std::vector<s16>& signal) { | 37 | void Filter::Process(std::vector<s16>& signal) { |
| 38 | const size_t num_frames = signal.size() / 2; | 38 | const std::size_t num_frames = signal.size() / 2; |
| 39 | for (size_t i = 0; i < num_frames; i++) { | 39 | for (std::size_t i = 0; i < num_frames; i++) { |
| 40 | std::rotate(in.begin(), in.end() - 1, in.end()); | 40 | std::rotate(in.begin(), in.end() - 1, in.end()); |
| 41 | std::rotate(out.begin(), out.end() - 1, out.end()); | 41 | std::rotate(out.begin(), out.end() - 1, out.end()); |
| 42 | 42 | ||
| 43 | for (size_t ch = 0; ch < channel_count; ch++) { | 43 | for (std::size_t ch = 0; ch < channel_count; ch++) { |
| 44 | in[0][ch] = signal[i * channel_count + ch]; | 44 | in[0][ch] = signal[i * channel_count + ch]; |
| 45 | 45 | ||
| 46 | out[0][ch] = b0 * in[0][ch] + b1 * in[1][ch] + b2 * in[2][ch] - a1 * out[1][ch] - | 46 | out[0][ch] = b0 * in[0][ch] + b1 * in[1][ch] + b2 * in[2][ch] - a1 * out[1][ch] - |
| @@ -54,14 +54,14 @@ void Filter::Process(std::vector<s16>& signal) { | |||
| 54 | /// Calculates the appropriate Q for each biquad in a cascading filter. | 54 | /// Calculates the appropriate Q for each biquad in a cascading filter. |
| 55 | /// @param total_count The total number of biquads to be cascaded. | 55 | /// @param total_count The total number of biquads to be cascaded. |
| 56 | /// @param index 0-index of the biquad to calculate the Q value for. | 56 | /// @param index 0-index of the biquad to calculate the Q value for. |
| 57 | static double CascadingBiquadQ(size_t total_count, size_t index) { | 57 | static double CascadingBiquadQ(std::size_t total_count, std::size_t index) { |
| 58 | const double pole = M_PI * (2 * index + 1) / (4.0 * total_count); | 58 | const double pole = M_PI * (2 * index + 1) / (4.0 * total_count); |
| 59 | return 1.0 / (2.0 * std::cos(pole)); | 59 | return 1.0 / (2.0 * std::cos(pole)); |
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | CascadingFilter CascadingFilter::LowPass(double cutoff, size_t cascade_size) { | 62 | CascadingFilter CascadingFilter::LowPass(double cutoff, std::size_t cascade_size) { |
| 63 | std::vector<Filter> cascade(cascade_size); | 63 | std::vector<Filter> cascade(cascade_size); |
| 64 | for (size_t i = 0; i < cascade_size; i++) { | 64 | for (std::size_t i = 0; i < cascade_size; i++) { |
| 65 | cascade[i] = Filter::LowPass(cutoff, CascadingBiquadQ(cascade_size, i)); | 65 | cascade[i] = Filter::LowPass(cutoff, CascadingBiquadQ(cascade_size, i)); |
| 66 | } | 66 | } |
| 67 | return CascadingFilter{std::move(cascade)}; | 67 | return CascadingFilter{std::move(cascade)}; |
diff --git a/src/audio_core/algorithm/filter.h b/src/audio_core/algorithm/filter.h index a41beef98..3546d149b 100644 --- a/src/audio_core/algorithm/filter.h +++ b/src/audio_core/algorithm/filter.h | |||
| @@ -30,7 +30,7 @@ public: | |||
| 30 | void Process(std::vector<s16>& signal); | 30 | void Process(std::vector<s16>& signal); |
| 31 | 31 | ||
| 32 | private: | 32 | private: |
| 33 | static constexpr size_t channel_count = 2; | 33 | static constexpr std::size_t channel_count = 2; |
| 34 | 34 | ||
| 35 | /// Coefficients are in normalized form (a0 = 1.0). | 35 | /// Coefficients are in normalized form (a0 = 1.0). |
| 36 | double a1, a2, b0, b1, b2; | 36 | double a1, a2, b0, b1, b2; |
| @@ -46,7 +46,7 @@ public: | |||
| 46 | /// Creates a cascading low-pass filter. | 46 | /// Creates a cascading low-pass filter. |
| 47 | /// @param cutoff Determines the cutoff frequency. A value from 0.0 to 1.0. | 47 | /// @param cutoff Determines the cutoff frequency. A value from 0.0 to 1.0. |
| 48 | /// @param cascade_size Number of biquads in cascade. | 48 | /// @param cascade_size Number of biquads in cascade. |
| 49 | static CascadingFilter LowPass(double cutoff, size_t cascade_size); | 49 | static CascadingFilter LowPass(double cutoff, std::size_t cascade_size); |
| 50 | 50 | ||
| 51 | /// Passthrough. | 51 | /// Passthrough. |
| 52 | CascadingFilter(); | 52 | CascadingFilter(); |
diff --git a/src/audio_core/algorithm/interpolate.cpp b/src/audio_core/algorithm/interpolate.cpp index 11459821f..3aea9b0f2 100644 --- a/src/audio_core/algorithm/interpolate.cpp +++ b/src/audio_core/algorithm/interpolate.cpp | |||
| @@ -14,7 +14,7 @@ | |||
| 14 | namespace AudioCore { | 14 | namespace AudioCore { |
| 15 | 15 | ||
| 16 | /// The Lanczos kernel | 16 | /// The Lanczos kernel |
| 17 | static double Lanczos(size_t a, double x) { | 17 | static double Lanczos(std::size_t a, double x) { |
| 18 | if (x == 0.0) | 18 | if (x == 0.0) |
| 19 | return 1.0; | 19 | return 1.0; |
| 20 | const double px = M_PI * x; | 20 | const double px = M_PI * x; |
| @@ -37,15 +37,15 @@ std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input, | |||
| 37 | } | 37 | } |
| 38 | state.nyquist.Process(input); | 38 | state.nyquist.Process(input); |
| 39 | 39 | ||
| 40 | constexpr size_t taps = InterpolationState::lanczos_taps; | 40 | constexpr std::size_t taps = InterpolationState::lanczos_taps; |
| 41 | const size_t num_frames = input.size() / 2; | 41 | const std::size_t num_frames = input.size() / 2; |
| 42 | 42 | ||
| 43 | std::vector<s16> output; | 43 | std::vector<s16> output; |
| 44 | output.reserve(static_cast<size_t>(input.size() / ratio + 4)); | 44 | output.reserve(static_cast<std::size_t>(input.size() / ratio + 4)); |
| 45 | 45 | ||
| 46 | double& pos = state.position; | 46 | double& pos = state.position; |
| 47 | auto& h = state.history; | 47 | auto& h = state.history; |
| 48 | for (size_t i = 0; i < num_frames; ++i) { | 48 | for (std::size_t i = 0; i < num_frames; ++i) { |
| 49 | std::rotate(h.begin(), h.end() - 1, h.end()); | 49 | std::rotate(h.begin(), h.end() - 1, h.end()); |
| 50 | h[0][0] = input[i * 2 + 0]; | 50 | h[0][0] = input[i * 2 + 0]; |
| 51 | h[0][1] = input[i * 2 + 1]; | 51 | h[0][1] = input[i * 2 + 1]; |
| @@ -53,7 +53,7 @@ std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input, | |||
| 53 | while (pos <= 1.0) { | 53 | while (pos <= 1.0) { |
| 54 | double l = 0.0; | 54 | double l = 0.0; |
| 55 | double r = 0.0; | 55 | double r = 0.0; |
| 56 | for (size_t j = 0; j < h.size(); j++) { | 56 | for (std::size_t j = 0; j < h.size(); j++) { |
| 57 | l += Lanczos(taps, pos + j - taps + 1) * h[j][0]; | 57 | l += Lanczos(taps, pos + j - taps + 1) * h[j][0]; |
| 58 | r += Lanczos(taps, pos + j - taps + 1) * h[j][1]; | 58 | r += Lanczos(taps, pos + j - taps + 1) * h[j][1]; |
| 59 | } | 59 | } |
diff --git a/src/audio_core/algorithm/interpolate.h b/src/audio_core/algorithm/interpolate.h index c79c2eef4..edbd6460f 100644 --- a/src/audio_core/algorithm/interpolate.h +++ b/src/audio_core/algorithm/interpolate.h | |||
| @@ -12,8 +12,8 @@ | |||
| 12 | namespace AudioCore { | 12 | namespace AudioCore { |
| 13 | 13 | ||
| 14 | struct InterpolationState { | 14 | struct InterpolationState { |
| 15 | static constexpr size_t lanczos_taps = 4; | 15 | static constexpr std::size_t lanczos_taps = 4; |
| 16 | static constexpr size_t history_size = lanczos_taps * 2 - 1; | 16 | static constexpr std::size_t history_size = lanczos_taps * 2 - 1; |
| 17 | 17 | ||
| 18 | double current_ratio = 0.0; | 18 | double current_ratio = 0.0; |
| 19 | CascadingFilter nyquist; | 19 | CascadingFilter nyquist; |
diff --git a/src/audio_core/audio_out.cpp b/src/audio_core/audio_out.cpp index 12632a95c..0c8f5b18e 100644 --- a/src/audio_core/audio_out.cpp +++ b/src/audio_core/audio_out.cpp | |||
| @@ -39,7 +39,8 @@ StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels, std::string&& | |||
| 39 | sink->AcquireSinkStream(sample_rate, num_channels, name), std::move(name)); | 39 | sink->AcquireSinkStream(sample_rate, num_channels, name), std::move(name)); |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count) { | 42 | std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream, |
| 43 | std::size_t max_count) { | ||
| 43 | return stream->GetTagsAndReleaseBuffers(max_count); | 44 | return stream->GetTagsAndReleaseBuffers(max_count); |
| 44 | } | 45 | } |
| 45 | 46 | ||
diff --git a/src/audio_core/audio_out.h b/src/audio_core/audio_out.h index 39b7e656b..df9607ac7 100644 --- a/src/audio_core/audio_out.h +++ b/src/audio_core/audio_out.h | |||
| @@ -25,7 +25,7 @@ public: | |||
| 25 | Stream::ReleaseCallback&& release_callback); | 25 | Stream::ReleaseCallback&& release_callback); |
| 26 | 26 | ||
| 27 | /// Returns a vector of recently released buffers specified by tag for the specified stream | 27 | /// Returns a vector of recently released buffers specified by tag for the specified stream |
| 28 | std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count); | 28 | std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(StreamPtr stream, std::size_t max_count); |
| 29 | 29 | ||
| 30 | /// Starts an audio stream for playback | 30 | /// Starts an audio stream for playback |
| 31 | void StartStream(StreamPtr stream); | 31 | void StartStream(StreamPtr stream); |
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp index a75cd3be5..6f0ff953a 100644 --- a/src/audio_core/audio_renderer.cpp +++ b/src/audio_core/audio_renderer.cpp | |||
| @@ -3,9 +3,12 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "audio_core/algorithm/interpolate.h" | 5 | #include "audio_core/algorithm/interpolate.h" |
| 6 | #include "audio_core/audio_out.h" | ||
| 6 | #include "audio_core/audio_renderer.h" | 7 | #include "audio_core/audio_renderer.h" |
| 8 | #include "audio_core/codec.h" | ||
| 7 | #include "common/assert.h" | 9 | #include "common/assert.h" |
| 8 | #include "common/logging/log.h" | 10 | #include "common/logging/log.h" |
| 11 | #include "core/hle/kernel/event.h" | ||
| 9 | #include "core/memory.h" | 12 | #include "core/memory.h" |
| 10 | 13 | ||
| 11 | namespace AudioCore { | 14 | namespace AudioCore { |
| @@ -13,6 +16,41 @@ namespace AudioCore { | |||
| 13 | constexpr u32 STREAM_SAMPLE_RATE{48000}; | 16 | constexpr u32 STREAM_SAMPLE_RATE{48000}; |
| 14 | constexpr u32 STREAM_NUM_CHANNELS{2}; | 17 | constexpr u32 STREAM_NUM_CHANNELS{2}; |
| 15 | 18 | ||
| 19 | class AudioRenderer::VoiceState { | ||
| 20 | public: | ||
| 21 | bool IsPlaying() const { | ||
| 22 | return is_in_use && info.play_state == PlayState::Started; | ||
| 23 | } | ||
| 24 | |||
| 25 | const VoiceOutStatus& GetOutStatus() const { | ||
| 26 | return out_status; | ||
| 27 | } | ||
| 28 | |||
| 29 | const VoiceInfo& GetInfo() const { | ||
| 30 | return info; | ||
| 31 | } | ||
| 32 | |||
| 33 | VoiceInfo& Info() { | ||
| 34 | return info; | ||
| 35 | } | ||
| 36 | |||
| 37 | void SetWaveIndex(std::size_t index); | ||
| 38 | std::vector<s16> DequeueSamples(std::size_t sample_count); | ||
| 39 | void UpdateState(); | ||
| 40 | void RefreshBuffer(); | ||
| 41 | |||
| 42 | private: | ||
| 43 | bool is_in_use{}; | ||
| 44 | bool is_refresh_pending{}; | ||
| 45 | std::size_t wave_index{}; | ||
| 46 | std::size_t offset{}; | ||
| 47 | Codec::ADPCMState adpcm_state{}; | ||
| 48 | InterpolationState interp_state{}; | ||
| 49 | std::vector<s16> samples; | ||
| 50 | VoiceOutStatus out_status{}; | ||
| 51 | VoiceInfo info{}; | ||
| 52 | }; | ||
| 53 | |||
| 16 | AudioRenderer::AudioRenderer(AudioRendererParameter params, | 54 | AudioRenderer::AudioRenderer(AudioRendererParameter params, |
| 17 | Kernel::SharedPtr<Kernel::Event> buffer_event) | 55 | Kernel::SharedPtr<Kernel::Event> buffer_event) |
| 18 | : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count) { | 56 | : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count) { |
| @@ -27,6 +65,8 @@ AudioRenderer::AudioRenderer(AudioRendererParameter params, | |||
| 27 | QueueMixedBuffer(2); | 65 | QueueMixedBuffer(2); |
| 28 | } | 66 | } |
| 29 | 67 | ||
| 68 | AudioRenderer::~AudioRenderer() = default; | ||
| 69 | |||
| 30 | u32 AudioRenderer::GetSampleRate() const { | 70 | u32 AudioRenderer::GetSampleRate() const { |
| 31 | return worker_params.sample_rate; | 71 | return worker_params.sample_rate; |
| 32 | } | 72 | } |
| @@ -39,6 +79,10 @@ u32 AudioRenderer::GetMixBufferCount() const { | |||
| 39 | return worker_params.mix_buffer_count; | 79 | return worker_params.mix_buffer_count; |
| 40 | } | 80 | } |
| 41 | 81 | ||
| 82 | Stream::State AudioRenderer::GetStreamState() const { | ||
| 83 | return stream->GetState(); | ||
| 84 | } | ||
| 85 | |||
| 42 | std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) { | 86 | std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) { |
| 43 | // Copy UpdateDataHeader struct | 87 | // Copy UpdateDataHeader struct |
| 44 | UpdateDataHeader config{}; | 88 | UpdateDataHeader config{}; |
| @@ -52,8 +96,8 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_ | |||
| 52 | memory_pool_count * sizeof(MemoryPoolInfo)); | 96 | memory_pool_count * sizeof(MemoryPoolInfo)); |
| 53 | 97 | ||
| 54 | // Copy VoiceInfo structs | 98 | // Copy VoiceInfo structs |
| 55 | size_t offset{sizeof(UpdateDataHeader) + config.behavior_size + config.memory_pools_size + | 99 | std::size_t offset{sizeof(UpdateDataHeader) + config.behavior_size + config.memory_pools_size + |
| 56 | config.voice_resource_size}; | 100 | config.voice_resource_size}; |
| 57 | for (auto& voice : voices) { | 101 | for (auto& voice : voices) { |
| 58 | std::memcpy(&voice.Info(), input_params.data() + offset, sizeof(VoiceInfo)); | 102 | std::memcpy(&voice.Info(), input_params.data() + offset, sizeof(VoiceInfo)); |
| 59 | offset += sizeof(VoiceInfo); | 103 | offset += sizeof(VoiceInfo); |
| @@ -72,7 +116,7 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_ | |||
| 72 | 116 | ||
| 73 | // Update memory pool state | 117 | // Update memory pool state |
| 74 | std::vector<MemoryPoolEntry> memory_pool(memory_pool_count); | 118 | std::vector<MemoryPoolEntry> memory_pool(memory_pool_count); |
| 75 | for (size_t index = 0; index < memory_pool.size(); ++index) { | 119 | for (std::size_t index = 0; index < memory_pool.size(); ++index) { |
| 76 | if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestAttach) { | 120 | if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestAttach) { |
| 77 | memory_pool[index].state = MemoryPoolStates::Attached; | 121 | memory_pool[index].state = MemoryPoolStates::Attached; |
| 78 | } else if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestDetach) { | 122 | } else if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestDetach) { |
| @@ -93,7 +137,7 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_ | |||
| 93 | response_data.memory_pools_size); | 137 | response_data.memory_pools_size); |
| 94 | 138 | ||
| 95 | // Copy output voice status | 139 | // Copy output voice status |
| 96 | size_t voice_out_status_offset{sizeof(UpdateDataHeader) + response_data.memory_pools_size}; | 140 | std::size_t voice_out_status_offset{sizeof(UpdateDataHeader) + response_data.memory_pools_size}; |
| 97 | for (const auto& voice : voices) { | 141 | for (const auto& voice : voices) { |
| 98 | std::memcpy(output_params.data() + voice_out_status_offset, &voice.GetOutStatus(), | 142 | std::memcpy(output_params.data() + voice_out_status_offset, &voice.GetOutStatus(), |
| 99 | sizeof(VoiceOutStatus)); | 143 | sizeof(VoiceOutStatus)); |
| @@ -103,12 +147,12 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_ | |||
| 103 | return output_params; | 147 | return output_params; |
| 104 | } | 148 | } |
| 105 | 149 | ||
| 106 | void AudioRenderer::VoiceState::SetWaveIndex(size_t index) { | 150 | void AudioRenderer::VoiceState::SetWaveIndex(std::size_t index) { |
| 107 | wave_index = index & 3; | 151 | wave_index = index & 3; |
| 108 | is_refresh_pending = true; | 152 | is_refresh_pending = true; |
| 109 | } | 153 | } |
| 110 | 154 | ||
| 111 | std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(size_t sample_count) { | 155 | std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(std::size_t sample_count) { |
| 112 | if (!IsPlaying()) { | 156 | if (!IsPlaying()) { |
| 113 | return {}; | 157 | return {}; |
| 114 | } | 158 | } |
| @@ -117,9 +161,9 @@ std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(size_t sample_count) | |||
| 117 | RefreshBuffer(); | 161 | RefreshBuffer(); |
| 118 | } | 162 | } |
| 119 | 163 | ||
| 120 | const size_t max_size{samples.size() - offset}; | 164 | const std::size_t max_size{samples.size() - offset}; |
| 121 | const size_t dequeue_offset{offset}; | 165 | const std::size_t dequeue_offset{offset}; |
| 122 | size_t size{sample_count * STREAM_NUM_CHANNELS}; | 166 | std::size_t size{sample_count * STREAM_NUM_CHANNELS}; |
| 123 | if (size > max_size) { | 167 | if (size > max_size) { |
| 124 | size = max_size; | 168 | size = max_size; |
| 125 | } | 169 | } |
| @@ -184,7 +228,7 @@ void AudioRenderer::VoiceState::RefreshBuffer() { | |||
| 184 | case 1: | 228 | case 1: |
| 185 | // 1 channel is upsampled to 2 channel | 229 | // 1 channel is upsampled to 2 channel |
| 186 | samples.resize(new_samples.size() * 2); | 230 | samples.resize(new_samples.size() * 2); |
| 187 | for (size_t index = 0; index < new_samples.size(); ++index) { | 231 | for (std::size_t index = 0; index < new_samples.size(); ++index) { |
| 188 | samples[index * 2] = new_samples[index]; | 232 | samples[index * 2] = new_samples[index]; |
| 189 | samples[index * 2 + 1] = new_samples[index]; | 233 | samples[index * 2 + 1] = new_samples[index]; |
| 190 | } | 234 | } |
| @@ -210,7 +254,7 @@ static constexpr s16 ClampToS16(s32 value) { | |||
| 210 | } | 254 | } |
| 211 | 255 | ||
| 212 | void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) { | 256 | void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) { |
| 213 | constexpr size_t BUFFER_SIZE{512}; | 257 | constexpr std::size_t BUFFER_SIZE{512}; |
| 214 | std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels()); | 258 | std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels()); |
| 215 | 259 | ||
| 216 | for (auto& voice : voices) { | 260 | for (auto& voice : voices) { |
| @@ -218,7 +262,7 @@ void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) { | |||
| 218 | continue; | 262 | continue; |
| 219 | } | 263 | } |
| 220 | 264 | ||
| 221 | size_t offset{}; | 265 | std::size_t offset{}; |
| 222 | s64 samples_remaining{BUFFER_SIZE}; | 266 | s64 samples_remaining{BUFFER_SIZE}; |
| 223 | while (samples_remaining > 0) { | 267 | while (samples_remaining > 0) { |
| 224 | const std::vector<s16> samples{voice.DequeueSamples(samples_remaining)}; | 268 | const std::vector<s16> samples{voice.DequeueSamples(samples_remaining)}; |
diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h index 6d069d693..dfef89e1d 100644 --- a/src/audio_core/audio_renderer.h +++ b/src/audio_core/audio_renderer.h | |||
| @@ -8,16 +8,20 @@ | |||
| 8 | #include <memory> | 8 | #include <memory> |
| 9 | #include <vector> | 9 | #include <vector> |
| 10 | 10 | ||
| 11 | #include "audio_core/algorithm/interpolate.h" | ||
| 12 | #include "audio_core/audio_out.h" | ||
| 13 | #include "audio_core/codec.h" | ||
| 14 | #include "audio_core/stream.h" | 11 | #include "audio_core/stream.h" |
| 12 | #include "common/common_funcs.h" | ||
| 15 | #include "common/common_types.h" | 13 | #include "common/common_types.h" |
| 16 | #include "common/swap.h" | 14 | #include "common/swap.h" |
| 17 | #include "core/hle/kernel/event.h" | 15 | #include "core/hle/kernel/object.h" |
| 16 | |||
| 17 | namespace Kernel { | ||
| 18 | class Event; | ||
| 19 | } | ||
| 18 | 20 | ||
| 19 | namespace AudioCore { | 21 | namespace AudioCore { |
| 20 | 22 | ||
| 23 | class AudioOut; | ||
| 24 | |||
| 21 | enum class PlayState : u8 { | 25 | enum class PlayState : u8 { |
| 22 | Started = 0, | 26 | Started = 0, |
| 23 | Stopped = 1, | 27 | Stopped = 1, |
| @@ -158,53 +162,23 @@ static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size | |||
| 158 | class AudioRenderer { | 162 | class AudioRenderer { |
| 159 | public: | 163 | public: |
| 160 | AudioRenderer(AudioRendererParameter params, Kernel::SharedPtr<Kernel::Event> buffer_event); | 164 | AudioRenderer(AudioRendererParameter params, Kernel::SharedPtr<Kernel::Event> buffer_event); |
| 165 | ~AudioRenderer(); | ||
| 166 | |||
| 161 | std::vector<u8> UpdateAudioRenderer(const std::vector<u8>& input_params); | 167 | std::vector<u8> UpdateAudioRenderer(const std::vector<u8>& input_params); |
| 162 | void QueueMixedBuffer(Buffer::Tag tag); | 168 | void QueueMixedBuffer(Buffer::Tag tag); |
| 163 | void ReleaseAndQueueBuffers(); | 169 | void ReleaseAndQueueBuffers(); |
| 164 | u32 GetSampleRate() const; | 170 | u32 GetSampleRate() const; |
| 165 | u32 GetSampleCount() const; | 171 | u32 GetSampleCount() const; |
| 166 | u32 GetMixBufferCount() const; | 172 | u32 GetMixBufferCount() const; |
| 173 | Stream::State GetStreamState() const; | ||
| 167 | 174 | ||
| 168 | private: | 175 | private: |
| 169 | class VoiceState { | 176 | class VoiceState; |
| 170 | public: | ||
| 171 | bool IsPlaying() const { | ||
| 172 | return is_in_use && info.play_state == PlayState::Started; | ||
| 173 | } | ||
| 174 | |||
| 175 | const VoiceOutStatus& GetOutStatus() const { | ||
| 176 | return out_status; | ||
| 177 | } | ||
| 178 | |||
| 179 | const VoiceInfo& GetInfo() const { | ||
| 180 | return info; | ||
| 181 | } | ||
| 182 | |||
| 183 | VoiceInfo& Info() { | ||
| 184 | return info; | ||
| 185 | } | ||
| 186 | |||
| 187 | void SetWaveIndex(size_t index); | ||
| 188 | std::vector<s16> DequeueSamples(size_t sample_count); | ||
| 189 | void UpdateState(); | ||
| 190 | void RefreshBuffer(); | ||
| 191 | |||
| 192 | private: | ||
| 193 | bool is_in_use{}; | ||
| 194 | bool is_refresh_pending{}; | ||
| 195 | size_t wave_index{}; | ||
| 196 | size_t offset{}; | ||
| 197 | Codec::ADPCMState adpcm_state{}; | ||
| 198 | InterpolationState interp_state{}; | ||
| 199 | std::vector<s16> samples; | ||
| 200 | VoiceOutStatus out_status{}; | ||
| 201 | VoiceInfo info{}; | ||
| 202 | }; | ||
| 203 | 177 | ||
| 204 | AudioRendererParameter worker_params; | 178 | AudioRendererParameter worker_params; |
| 205 | Kernel::SharedPtr<Kernel::Event> buffer_event; | 179 | Kernel::SharedPtr<Kernel::Event> buffer_event; |
| 206 | std::vector<VoiceState> voices; | 180 | std::vector<VoiceState> voices; |
| 207 | std::unique_ptr<AudioCore::AudioOut> audio_out; | 181 | std::unique_ptr<AudioOut> audio_out; |
| 208 | AudioCore::StreamPtr stream; | 182 | AudioCore::StreamPtr stream; |
| 209 | }; | 183 | }; |
| 210 | 184 | ||
diff --git a/src/audio_core/codec.cpp b/src/audio_core/codec.cpp index c3021403f..454de798b 100644 --- a/src/audio_core/codec.cpp +++ b/src/audio_core/codec.cpp | |||
| @@ -8,27 +8,27 @@ | |||
| 8 | 8 | ||
| 9 | namespace AudioCore::Codec { | 9 | namespace AudioCore::Codec { |
| 10 | 10 | ||
| 11 | std::vector<s16> DecodeADPCM(const u8* const data, size_t size, const ADPCM_Coeff& coeff, | 11 | std::vector<s16> DecodeADPCM(const u8* const data, std::size_t size, const ADPCM_Coeff& coeff, |
| 12 | ADPCMState& state) { | 12 | ADPCMState& state) { |
| 13 | // GC-ADPCM with scale factor and variable coefficients. | 13 | // GC-ADPCM with scale factor and variable coefficients. |
| 14 | // Frames are 8 bytes long containing 14 samples each. | 14 | // Frames are 8 bytes long containing 14 samples each. |
| 15 | // Samples are 4 bits (one nibble) long. | 15 | // Samples are 4 bits (one nibble) long. |
| 16 | 16 | ||
| 17 | constexpr size_t FRAME_LEN = 8; | 17 | constexpr std::size_t FRAME_LEN = 8; |
| 18 | constexpr size_t SAMPLES_PER_FRAME = 14; | 18 | constexpr std::size_t SAMPLES_PER_FRAME = 14; |
| 19 | constexpr std::array<int, 16> SIGNED_NIBBLES = { | 19 | constexpr std::array<int, 16> SIGNED_NIBBLES = { |
| 20 | {0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1}}; | 20 | {0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1}}; |
| 21 | 21 | ||
| 22 | const size_t sample_count = (size / FRAME_LEN) * SAMPLES_PER_FRAME; | 22 | const std::size_t sample_count = (size / FRAME_LEN) * SAMPLES_PER_FRAME; |
| 23 | const size_t ret_size = | 23 | const std::size_t ret_size = |
| 24 | sample_count % 2 == 0 ? sample_count : sample_count + 1; // Ensure multiple of two. | 24 | sample_count % 2 == 0 ? sample_count : sample_count + 1; // Ensure multiple of two. |
| 25 | std::vector<s16> ret(ret_size); | 25 | std::vector<s16> ret(ret_size); |
| 26 | 26 | ||
| 27 | int yn1 = state.yn1, yn2 = state.yn2; | 27 | int yn1 = state.yn1, yn2 = state.yn2; |
| 28 | 28 | ||
| 29 | const size_t NUM_FRAMES = | 29 | const std::size_t NUM_FRAMES = |
| 30 | (sample_count + (SAMPLES_PER_FRAME - 1)) / SAMPLES_PER_FRAME; // Round up. | 30 | (sample_count + (SAMPLES_PER_FRAME - 1)) / SAMPLES_PER_FRAME; // Round up. |
| 31 | for (size_t framei = 0; framei < NUM_FRAMES; framei++) { | 31 | for (std::size_t framei = 0; framei < NUM_FRAMES; framei++) { |
| 32 | const int frame_header = data[framei * FRAME_LEN]; | 32 | const int frame_header = data[framei * FRAME_LEN]; |
| 33 | const int scale = 1 << (frame_header & 0xF); | 33 | const int scale = 1 << (frame_header & 0xF); |
| 34 | const int idx = (frame_header >> 4) & 0x7; | 34 | const int idx = (frame_header >> 4) & 0x7; |
| @@ -53,9 +53,9 @@ std::vector<s16> DecodeADPCM(const u8* const data, size_t size, const ADPCM_Coef | |||
| 53 | return static_cast<s16>(val); | 53 | return static_cast<s16>(val); |
| 54 | }; | 54 | }; |
| 55 | 55 | ||
| 56 | size_t outputi = framei * SAMPLES_PER_FRAME; | 56 | std::size_t outputi = framei * SAMPLES_PER_FRAME; |
| 57 | size_t datai = framei * FRAME_LEN + 1; | 57 | std::size_t datai = framei * FRAME_LEN + 1; |
| 58 | for (size_t i = 0; i < SAMPLES_PER_FRAME && outputi < sample_count; i += 2) { | 58 | for (std::size_t i = 0; i < SAMPLES_PER_FRAME && outputi < sample_count; i += 2) { |
| 59 | const s16 sample1 = decode_sample(SIGNED_NIBBLES[data[datai] >> 4]); | 59 | const s16 sample1 = decode_sample(SIGNED_NIBBLES[data[datai] >> 4]); |
| 60 | ret[outputi] = sample1; | 60 | ret[outputi] = sample1; |
| 61 | outputi++; | 61 | outputi++; |
diff --git a/src/audio_core/codec.h b/src/audio_core/codec.h index 3f845c42c..ef2ce01a8 100644 --- a/src/audio_core/codec.h +++ b/src/audio_core/codec.h | |||
| @@ -38,7 +38,7 @@ using ADPCM_Coeff = std::array<s16, 16>; | |||
| 38 | * @param state ADPCM state, this is updated with new state | 38 | * @param state ADPCM state, this is updated with new state |
| 39 | * @return Decoded stereo signed PCM16 data, sample_count in length | 39 | * @return Decoded stereo signed PCM16 data, sample_count in length |
| 40 | */ | 40 | */ |
| 41 | std::vector<s16> DecodeADPCM(const u8* const data, size_t size, const ADPCM_Coeff& coeff, | 41 | std::vector<s16> DecodeADPCM(const u8* const data, std::size_t size, const ADPCM_Coeff& coeff, |
| 42 | ADPCMState& state); | 42 | ADPCMState& state); |
| 43 | 43 | ||
| 44 | }; // namespace AudioCore::Codec | 44 | }; // namespace AudioCore::Codec |
diff --git a/src/audio_core/cubeb_sink.cpp b/src/audio_core/cubeb_sink.cpp index 79155a7a0..392039688 100644 --- a/src/audio_core/cubeb_sink.cpp +++ b/src/audio_core/cubeb_sink.cpp | |||
| @@ -63,8 +63,8 @@ public: | |||
| 63 | // Downsample 6 channels to 2 | 63 | // Downsample 6 channels to 2 |
| 64 | std::vector<s16> buf; | 64 | std::vector<s16> buf; |
| 65 | buf.reserve(samples.size() * num_channels / source_num_channels); | 65 | buf.reserve(samples.size() * num_channels / source_num_channels); |
| 66 | for (size_t i = 0; i < samples.size(); i += source_num_channels) { | 66 | for (std::size_t i = 0; i < samples.size(); i += source_num_channels) { |
| 67 | for (size_t ch = 0; ch < num_channels; ch++) { | 67 | for (std::size_t ch = 0; ch < num_channels; ch++) { |
| 68 | buf.push_back(samples[i + ch]); | 68 | buf.push_back(samples[i + ch]); |
| 69 | } | 69 | } |
| 70 | } | 70 | } |
| @@ -75,7 +75,7 @@ public: | |||
| 75 | queue.Push(samples); | 75 | queue.Push(samples); |
| 76 | } | 76 | } |
| 77 | 77 | ||
| 78 | size_t SamplesInQueue(u32 num_channels) const override { | 78 | std::size_t SamplesInQueue(u32 num_channels) const override { |
| 79 | if (!ctx) | 79 | if (!ctx) |
| 80 | return 0; | 80 | return 0; |
| 81 | 81 | ||
| @@ -119,10 +119,10 @@ CubebSink::CubebSink(std::string target_device_name) { | |||
| 119 | LOG_WARNING(Audio_Sink, "Audio output device enumeration not supported"); | 119 | LOG_WARNING(Audio_Sink, "Audio output device enumeration not supported"); |
| 120 | } else { | 120 | } else { |
| 121 | const auto collection_end{collection.device + collection.count}; | 121 | const auto collection_end{collection.device + collection.count}; |
| 122 | const auto device{std::find_if(collection.device, collection_end, | 122 | const auto device{ |
| 123 | [&](const cubeb_device_info& device) { | 123 | std::find_if(collection.device, collection_end, [&](const cubeb_device_info& info) { |
| 124 | return target_device_name == device.friendly_name; | 124 | return target_device_name == info.friendly_name; |
| 125 | })}; | 125 | })}; |
| 126 | if (device != collection_end) { | 126 | if (device != collection_end) { |
| 127 | output_device = device->devid; | 127 | output_device = device->devid; |
| 128 | } | 128 | } |
| @@ -159,15 +159,16 @@ long CubebSinkStream::DataCallback(cubeb_stream* stream, void* user_data, const | |||
| 159 | return {}; | 159 | return {}; |
| 160 | } | 160 | } |
| 161 | 161 | ||
| 162 | const size_t num_channels = impl->GetNumChannels(); | 162 | const std::size_t num_channels = impl->GetNumChannels(); |
| 163 | const size_t samples_to_write = num_channels * num_frames; | 163 | const std::size_t samples_to_write = num_channels * num_frames; |
| 164 | size_t samples_written; | 164 | std::size_t samples_written; |
| 165 | 165 | ||
| 166 | if (Settings::values.enable_audio_stretching) { | 166 | if (Settings::values.enable_audio_stretching) { |
| 167 | const std::vector<s16> in{impl->queue.Pop()}; | 167 | const std::vector<s16> in{impl->queue.Pop()}; |
| 168 | const size_t num_in{in.size() / num_channels}; | 168 | const std::size_t num_in{in.size() / num_channels}; |
| 169 | s16* const out{reinterpret_cast<s16*>(buffer)}; | 169 | s16* const out{reinterpret_cast<s16*>(buffer)}; |
| 170 | const size_t out_frames = impl->time_stretch.Process(in.data(), num_in, out, num_frames); | 170 | const std::size_t out_frames = |
| 171 | impl->time_stretch.Process(in.data(), num_in, out, num_frames); | ||
| 171 | samples_written = out_frames * num_channels; | 172 | samples_written = out_frames * num_channels; |
| 172 | 173 | ||
| 173 | if (impl->should_flush) { | 174 | if (impl->should_flush) { |
| @@ -184,7 +185,7 @@ long CubebSinkStream::DataCallback(cubeb_stream* stream, void* user_data, const | |||
| 184 | } | 185 | } |
| 185 | 186 | ||
| 186 | // Fill the rest of the frames with last_frame | 187 | // Fill the rest of the frames with last_frame |
| 187 | for (size_t i = samples_written; i < samples_to_write; i += num_channels) { | 188 | for (std::size_t i = samples_written; i < samples_to_write; i += num_channels) { |
| 188 | std::memcpy(buffer + i * sizeof(s16), &impl->last_frame[0], num_channels * sizeof(s16)); | 189 | std::memcpy(buffer + i * sizeof(s16), &impl->last_frame[0], num_channels * sizeof(s16)); |
| 189 | } | 190 | } |
| 190 | 191 | ||
| @@ -197,7 +198,7 @@ std::vector<std::string> ListCubebSinkDevices() { | |||
| 197 | std::vector<std::string> device_list; | 198 | std::vector<std::string> device_list; |
| 198 | cubeb* ctx; | 199 | cubeb* ctx; |
| 199 | 200 | ||
| 200 | if (cubeb_init(&ctx, "Citra Device Enumerator", nullptr) != CUBEB_OK) { | 201 | if (cubeb_init(&ctx, "yuzu Device Enumerator", nullptr) != CUBEB_OK) { |
| 201 | LOG_CRITICAL(Audio_Sink, "cubeb_init failed"); | 202 | LOG_CRITICAL(Audio_Sink, "cubeb_init failed"); |
| 202 | return {}; | 203 | return {}; |
| 203 | } | 204 | } |
| @@ -206,7 +207,7 @@ std::vector<std::string> ListCubebSinkDevices() { | |||
| 206 | if (cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection) != CUBEB_OK) { | 207 | if (cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection) != CUBEB_OK) { |
| 207 | LOG_WARNING(Audio_Sink, "Audio output device enumeration not supported"); | 208 | LOG_WARNING(Audio_Sink, "Audio output device enumeration not supported"); |
| 208 | } else { | 209 | } else { |
| 209 | for (size_t i = 0; i < collection.count; i++) { | 210 | for (std::size_t i = 0; i < collection.count; i++) { |
| 210 | const cubeb_device_info& device = collection.device[i]; | 211 | const cubeb_device_info& device = collection.device[i]; |
| 211 | if (device.friendly_name) { | 212 | if (device.friendly_name) { |
| 212 | device_list.emplace_back(device.friendly_name); | 213 | device_list.emplace_back(device.friendly_name); |
diff --git a/src/audio_core/null_sink.h b/src/audio_core/null_sink.h index 2ed0c83b6..a78d78893 100644 --- a/src/audio_core/null_sink.h +++ b/src/audio_core/null_sink.h | |||
| @@ -22,7 +22,7 @@ private: | |||
| 22 | struct NullSinkStreamImpl final : SinkStream { | 22 | struct NullSinkStreamImpl final : SinkStream { |
| 23 | void EnqueueSamples(u32 /*num_channels*/, const std::vector<s16>& /*samples*/) override {} | 23 | void EnqueueSamples(u32 /*num_channels*/, const std::vector<s16>& /*samples*/) override {} |
| 24 | 24 | ||
| 25 | size_t SamplesInQueue(u32 /*num_channels*/) const override { | 25 | std::size_t SamplesInQueue(u32 /*num_channels*/) const override { |
| 26 | return 0; | 26 | return 0; |
| 27 | } | 27 | } |
| 28 | 28 | ||
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp index 84dcdd98d..742a5e0a0 100644 --- a/src/audio_core/stream.cpp +++ b/src/audio_core/stream.cpp | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | 7 | ||
| 8 | #include "audio_core/sink.h" | 8 | #include "audio_core/sink.h" |
| 9 | #include "audio_core/sink_details.h" | 9 | #include "audio_core/sink_details.h" |
| 10 | #include "audio_core/sink_stream.h" | ||
| 10 | #include "audio_core/stream.h" | 11 | #include "audio_core/stream.h" |
| 11 | #include "common/assert.h" | 12 | #include "common/assert.h" |
| 12 | #include "common/logging/log.h" | 13 | #include "common/logging/log.h" |
| @@ -17,7 +18,7 @@ | |||
| 17 | 18 | ||
| 18 | namespace AudioCore { | 19 | namespace AudioCore { |
| 19 | 20 | ||
| 20 | constexpr size_t MaxAudioBufferCount{32}; | 21 | constexpr std::size_t MaxAudioBufferCount{32}; |
| 21 | 22 | ||
| 22 | u32 Stream::GetNumChannels() const { | 23 | u32 Stream::GetNumChannels() const { |
| 23 | switch (format) { | 24 | switch (format) { |
| @@ -48,11 +49,16 @@ void Stream::Play() { | |||
| 48 | } | 49 | } |
| 49 | 50 | ||
| 50 | void Stream::Stop() { | 51 | void Stream::Stop() { |
| 52 | state = State::Stopped; | ||
| 51 | ASSERT_MSG(false, "Unimplemented"); | 53 | ASSERT_MSG(false, "Unimplemented"); |
| 52 | } | 54 | } |
| 53 | 55 | ||
| 56 | Stream::State Stream::GetState() const { | ||
| 57 | return state; | ||
| 58 | } | ||
| 59 | |||
| 54 | s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const { | 60 | s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const { |
| 55 | const size_t num_samples{buffer.GetSamples().size() / GetNumChannels()}; | 61 | const std::size_t num_samples{buffer.GetSamples().size() / GetNumChannels()}; |
| 56 | return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate); | 62 | return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate); |
| 57 | } | 63 | } |
| 58 | 64 | ||
| @@ -122,9 +128,9 @@ bool Stream::ContainsBuffer(Buffer::Tag tag) const { | |||
| 122 | return {}; | 128 | return {}; |
| 123 | } | 129 | } |
| 124 | 130 | ||
| 125 | std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(size_t max_count) { | 131 | std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(std::size_t max_count) { |
| 126 | std::vector<Buffer::Tag> tags; | 132 | std::vector<Buffer::Tag> tags; |
| 127 | for (size_t count = 0; count < max_count && !released_buffers.empty(); ++count) { | 133 | for (std::size_t count = 0; count < max_count && !released_buffers.empty(); ++count) { |
| 128 | tags.push_back(released_buffers.front()->GetTag()); | 134 | tags.push_back(released_buffers.front()->GetTag()); |
| 129 | released_buffers.pop(); | 135 | released_buffers.pop(); |
| 130 | } | 136 | } |
diff --git a/src/audio_core/stream.h b/src/audio_core/stream.h index 049b92ca9..aebfeb51d 100644 --- a/src/audio_core/stream.h +++ b/src/audio_core/stream.h | |||
| @@ -11,13 +11,16 @@ | |||
| 11 | #include <queue> | 11 | #include <queue> |
| 12 | 12 | ||
| 13 | #include "audio_core/buffer.h" | 13 | #include "audio_core/buffer.h" |
| 14 | #include "audio_core/sink_stream.h" | ||
| 15 | #include "common/assert.h" | ||
| 16 | #include "common/common_types.h" | 14 | #include "common/common_types.h" |
| 17 | #include "core/core_timing.h" | 15 | |
| 16 | namespace CoreTiming { | ||
| 17 | struct EventType; | ||
| 18 | } | ||
| 18 | 19 | ||
| 19 | namespace AudioCore { | 20 | namespace AudioCore { |
| 20 | 21 | ||
| 22 | class SinkStream; | ||
| 23 | |||
| 21 | /** | 24 | /** |
| 22 | * Represents an audio stream, which is a sequence of queued buffers, to be outputed by AudioOut | 25 | * Represents an audio stream, which is a sequence of queued buffers, to be outputed by AudioOut |
| 23 | */ | 26 | */ |
| @@ -30,6 +33,12 @@ public: | |||
| 30 | Multi51Channel16, | 33 | Multi51Channel16, |
| 31 | }; | 34 | }; |
| 32 | 35 | ||
| 36 | /// Current state of the stream | ||
| 37 | enum class State { | ||
| 38 | Stopped, | ||
| 39 | Playing, | ||
| 40 | }; | ||
| 41 | |||
| 33 | /// Callback function type, used to change guest state on a buffer being released | 42 | /// Callback function type, used to change guest state on a buffer being released |
| 34 | using ReleaseCallback = std::function<void()>; | 43 | using ReleaseCallback = std::function<void()>; |
| 35 | 44 | ||
| @@ -49,7 +58,7 @@ public: | |||
| 49 | bool ContainsBuffer(Buffer::Tag tag) const; | 58 | bool ContainsBuffer(Buffer::Tag tag) const; |
| 50 | 59 | ||
| 51 | /// Returns a vector of recently released buffers specified by tag | 60 | /// Returns a vector of recently released buffers specified by tag |
| 52 | std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(size_t max_count); | 61 | std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(std::size_t max_count); |
| 53 | 62 | ||
| 54 | /// Returns true if the stream is currently playing | 63 | /// Returns true if the stream is currently playing |
| 55 | bool IsPlaying() const { | 64 | bool IsPlaying() const { |
| @@ -57,7 +66,7 @@ public: | |||
| 57 | } | 66 | } |
| 58 | 67 | ||
| 59 | /// Returns the number of queued buffers | 68 | /// Returns the number of queued buffers |
| 60 | size_t GetQueueSize() const { | 69 | std::size_t GetQueueSize() const { |
| 61 | return queued_buffers.size(); | 70 | return queued_buffers.size(); |
| 62 | } | 71 | } |
| 63 | 72 | ||
| @@ -69,13 +78,10 @@ public: | |||
| 69 | /// Gets the number of channels | 78 | /// Gets the number of channels |
| 70 | u32 GetNumChannels() const; | 79 | u32 GetNumChannels() const; |
| 71 | 80 | ||
| 72 | private: | 81 | /// Get the state |
| 73 | /// Current state of the stream | 82 | State GetState() const; |
| 74 | enum class State { | ||
| 75 | Stopped, | ||
| 76 | Playing, | ||
| 77 | }; | ||
| 78 | 83 | ||
| 84 | private: | ||
| 79 | /// Plays the next queued buffer in the audio stream, starting playback if necessary | 85 | /// Plays the next queued buffer in the audio stream, starting playback if necessary |
| 80 | void PlayNextBuffer(); | 86 | void PlayNextBuffer(); |
| 81 | 87 | ||
diff --git a/src/audio_core/time_stretch.cpp b/src/audio_core/time_stretch.cpp index da094c46b..d72d67994 100644 --- a/src/audio_core/time_stretch.cpp +++ b/src/audio_core/time_stretch.cpp | |||
| @@ -26,7 +26,8 @@ void TimeStretcher::Flush() { | |||
| 26 | m_sound_touch.flush(); | 26 | m_sound_touch.flush(); |
| 27 | } | 27 | } |
| 28 | 28 | ||
| 29 | size_t TimeStretcher::Process(const s16* in, size_t num_in, s16* out, size_t num_out) { | 29 | std::size_t TimeStretcher::Process(const s16* in, std::size_t num_in, s16* out, |
| 30 | std::size_t num_out) { | ||
| 30 | const double time_delta = static_cast<double>(num_out) / m_sample_rate; // seconds | 31 | const double time_delta = static_cast<double>(num_out) / m_sample_rate; // seconds |
| 31 | 32 | ||
| 32 | // We were given actual_samples number of samples, and num_samples were requested from us. | 33 | // We were given actual_samples number of samples, and num_samples were requested from us. |
| @@ -58,11 +59,11 @@ size_t TimeStretcher::Process(const s16* in, size_t num_in, s16* out, size_t num | |||
| 58 | m_stretch_ratio = std::max(m_stretch_ratio, 0.05); | 59 | m_stretch_ratio = std::max(m_stretch_ratio, 0.05); |
| 59 | m_sound_touch.setTempo(m_stretch_ratio); | 60 | m_sound_touch.setTempo(m_stretch_ratio); |
| 60 | 61 | ||
| 61 | LOG_DEBUG(Audio, "{:5}/{:5} ratio:{:0.6f} backlog:{:0.6f}", num_in, num_out, m_stretch_ratio, | 62 | LOG_TRACE(Audio, "{:5}/{:5} ratio:{:0.6f} backlog:{:0.6f}", num_in, num_out, m_stretch_ratio, |
| 62 | backlog_fullness); | 63 | backlog_fullness); |
| 63 | 64 | ||
| 64 | m_sound_touch.putSamples(in, num_in); | 65 | m_sound_touch.putSamples(in, static_cast<u32>(num_in)); |
| 65 | return m_sound_touch.receiveSamples(out, num_out); | 66 | return m_sound_touch.receiveSamples(out, static_cast<u32>(num_out)); |
| 66 | } | 67 | } |
| 67 | 68 | ||
| 68 | } // namespace AudioCore | 69 | } // namespace AudioCore |
diff --git a/src/audio_core/time_stretch.h b/src/audio_core/time_stretch.h index 7e39e695e..decd760f1 100644 --- a/src/audio_core/time_stretch.h +++ b/src/audio_core/time_stretch.h | |||
| @@ -4,7 +4,6 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | ||
| 8 | #include <cstddef> | 7 | #include <cstddef> |
| 9 | #include <SoundTouch.h> | 8 | #include <SoundTouch.h> |
| 10 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| @@ -20,7 +19,7 @@ public: | |||
| 20 | /// @param out Output sample buffer | 19 | /// @param out Output sample buffer |
| 21 | /// @param num_out Desired number of output frames in `out` | 20 | /// @param num_out Desired number of output frames in `out` |
| 22 | /// @returns Actual number of frames written to `out` | 21 | /// @returns Actual number of frames written to `out` |
| 23 | size_t Process(const s16* in, size_t num_in, s16* out, size_t num_out); | 22 | std::size_t Process(const s16* in, std::size_t num_in, s16* out, std::size_t num_out); |
| 24 | 23 | ||
| 25 | void Clear(); | 24 | void Clear(); |
| 26 | 25 | ||
diff --git a/src/common/alignment.h b/src/common/alignment.h index b9dd38746..225770fab 100644 --- a/src/common/alignment.h +++ b/src/common/alignment.h | |||
| @@ -8,13 +8,13 @@ | |||
| 8 | namespace Common { | 8 | namespace Common { |
| 9 | 9 | ||
| 10 | template <typename T> | 10 | template <typename T> |
| 11 | constexpr T AlignUp(T value, size_t size) { | 11 | constexpr T AlignUp(T value, std::size_t size) { |
| 12 | static_assert(std::is_unsigned_v<T>, "T must be an unsigned value."); | 12 | static_assert(std::is_unsigned_v<T>, "T must be an unsigned value."); |
| 13 | return static_cast<T>(value + (size - value % size) % size); | 13 | return static_cast<T>(value + (size - value % size) % size); |
| 14 | } | 14 | } |
| 15 | 15 | ||
| 16 | template <typename T> | 16 | template <typename T> |
| 17 | constexpr T AlignDown(T value, size_t size) { | 17 | constexpr T AlignDown(T value, std::size_t size) { |
| 18 | static_assert(std::is_unsigned_v<T>, "T must be an unsigned value."); | 18 | static_assert(std::is_unsigned_v<T>, "T must be an unsigned value."); |
| 19 | return static_cast<T>(value - value % size); | 19 | return static_cast<T>(value - value % size); |
| 20 | } | 20 | } |
diff --git a/src/common/bit_field.h b/src/common/bit_field.h index 732201de7..bf803da8d 100644 --- a/src/common/bit_field.h +++ b/src/common/bit_field.h | |||
| @@ -129,8 +129,8 @@ private: | |||
| 129 | 129 | ||
| 130 | public: | 130 | public: |
| 131 | /// Constants to allow limited introspection of fields if needed | 131 | /// Constants to allow limited introspection of fields if needed |
| 132 | static constexpr size_t position = Position; | 132 | static constexpr std::size_t position = Position; |
| 133 | static constexpr size_t bits = Bits; | 133 | static constexpr std::size_t bits = Bits; |
| 134 | static constexpr StorageType mask = (((StorageTypeU)~0) >> (8 * sizeof(T) - bits)) << position; | 134 | static constexpr StorageType mask = (((StorageTypeU)~0) >> (8 * sizeof(T) - bits)) << position; |
| 135 | 135 | ||
| 136 | /** | 136 | /** |
diff --git a/src/common/bit_set.h b/src/common/bit_set.h index 5a197d8c1..5cd1352b2 100644 --- a/src/common/bit_set.h +++ b/src/common/bit_set.h | |||
| @@ -170,14 +170,14 @@ public: | |||
| 170 | m_val |= (IntTy)1 << bit; | 170 | m_val |= (IntTy)1 << bit; |
| 171 | } | 171 | } |
| 172 | 172 | ||
| 173 | static BitSet AllTrue(size_t count) { | 173 | static BitSet AllTrue(std::size_t count) { |
| 174 | return BitSet(count == sizeof(IntTy) * 8 ? ~(IntTy)0 : (((IntTy)1 << count) - 1)); | 174 | return BitSet(count == sizeof(IntTy) * 8 ? ~(IntTy)0 : (((IntTy)1 << count) - 1)); |
| 175 | } | 175 | } |
| 176 | 176 | ||
| 177 | Ref operator[](size_t bit) { | 177 | Ref operator[](std::size_t bit) { |
| 178 | return Ref(this, (IntTy)1 << bit); | 178 | return Ref(this, (IntTy)1 << bit); |
| 179 | } | 179 | } |
| 180 | const Ref operator[](size_t bit) const { | 180 | const Ref operator[](std::size_t bit) const { |
| 181 | return (*const_cast<BitSet*>(this))[bit]; | 181 | return (*const_cast<BitSet*>(this))[bit]; |
| 182 | } | 182 | } |
| 183 | bool operator==(BitSet other) const { | 183 | bool operator==(BitSet other) const { |
diff --git a/src/common/cityhash.cpp b/src/common/cityhash.cpp index de31ffbd8..4e1d874b5 100644 --- a/src/common/cityhash.cpp +++ b/src/common/cityhash.cpp | |||
| @@ -114,7 +114,7 @@ static uint64 HashLen16(uint64 u, uint64 v, uint64 mul) { | |||
| 114 | return b; | 114 | return b; |
| 115 | } | 115 | } |
| 116 | 116 | ||
| 117 | static uint64 HashLen0to16(const char* s, size_t len) { | 117 | static uint64 HashLen0to16(const char* s, std::size_t len) { |
| 118 | if (len >= 8) { | 118 | if (len >= 8) { |
| 119 | uint64 mul = k2 + len * 2; | 119 | uint64 mul = k2 + len * 2; |
| 120 | uint64 a = Fetch64(s) + k2; | 120 | uint64 a = Fetch64(s) + k2; |
| @@ -141,7 +141,7 @@ static uint64 HashLen0to16(const char* s, size_t len) { | |||
| 141 | 141 | ||
| 142 | // This probably works well for 16-byte strings as well, but it may be overkill | 142 | // This probably works well for 16-byte strings as well, but it may be overkill |
| 143 | // in that case. | 143 | // in that case. |
| 144 | static uint64 HashLen17to32(const char* s, size_t len) { | 144 | static uint64 HashLen17to32(const char* s, std::size_t len) { |
| 145 | uint64 mul = k2 + len * 2; | 145 | uint64 mul = k2 + len * 2; |
| 146 | uint64 a = Fetch64(s) * k1; | 146 | uint64 a = Fetch64(s) * k1; |
| 147 | uint64 b = Fetch64(s + 8); | 147 | uint64 b = Fetch64(s + 8); |
| @@ -170,7 +170,7 @@ static pair<uint64, uint64> WeakHashLen32WithSeeds(const char* s, uint64 a, uint | |||
| 170 | } | 170 | } |
| 171 | 171 | ||
| 172 | // Return an 8-byte hash for 33 to 64 bytes. | 172 | // Return an 8-byte hash for 33 to 64 bytes. |
| 173 | static uint64 HashLen33to64(const char* s, size_t len) { | 173 | static uint64 HashLen33to64(const char* s, std::size_t len) { |
| 174 | uint64 mul = k2 + len * 2; | 174 | uint64 mul = k2 + len * 2; |
| 175 | uint64 a = Fetch64(s) * k2; | 175 | uint64 a = Fetch64(s) * k2; |
| 176 | uint64 b = Fetch64(s + 8); | 176 | uint64 b = Fetch64(s + 8); |
| @@ -191,7 +191,7 @@ static uint64 HashLen33to64(const char* s, size_t len) { | |||
| 191 | return b + x; | 191 | return b + x; |
| 192 | } | 192 | } |
| 193 | 193 | ||
| 194 | uint64 CityHash64(const char* s, size_t len) { | 194 | uint64 CityHash64(const char* s, std::size_t len) { |
| 195 | if (len <= 32) { | 195 | if (len <= 32) { |
| 196 | if (len <= 16) { | 196 | if (len <= 16) { |
| 197 | return HashLen0to16(s, len); | 197 | return HashLen0to16(s, len); |
| @@ -212,7 +212,7 @@ uint64 CityHash64(const char* s, size_t len) { | |||
| 212 | x = x * k1 + Fetch64(s); | 212 | x = x * k1 + Fetch64(s); |
| 213 | 213 | ||
| 214 | // Decrease len to the nearest multiple of 64, and operate on 64-byte chunks. | 214 | // Decrease len to the nearest multiple of 64, and operate on 64-byte chunks. |
| 215 | len = (len - 1) & ~static_cast<size_t>(63); | 215 | len = (len - 1) & ~static_cast<std::size_t>(63); |
| 216 | do { | 216 | do { |
| 217 | x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1; | 217 | x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1; |
| 218 | y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1; | 218 | y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1; |
| @@ -229,17 +229,17 @@ uint64 CityHash64(const char* s, size_t len) { | |||
| 229 | HashLen16(v.second, w.second) + x); | 229 | HashLen16(v.second, w.second) + x); |
| 230 | } | 230 | } |
| 231 | 231 | ||
| 232 | uint64 CityHash64WithSeed(const char* s, size_t len, uint64 seed) { | 232 | uint64 CityHash64WithSeed(const char* s, std::size_t len, uint64 seed) { |
| 233 | return CityHash64WithSeeds(s, len, k2, seed); | 233 | return CityHash64WithSeeds(s, len, k2, seed); |
| 234 | } | 234 | } |
| 235 | 235 | ||
| 236 | uint64 CityHash64WithSeeds(const char* s, size_t len, uint64 seed0, uint64 seed1) { | 236 | uint64 CityHash64WithSeeds(const char* s, std::size_t len, uint64 seed0, uint64 seed1) { |
| 237 | return HashLen16(CityHash64(s, len) - seed0, seed1); | 237 | return HashLen16(CityHash64(s, len) - seed0, seed1); |
| 238 | } | 238 | } |
| 239 | 239 | ||
| 240 | // A subroutine for CityHash128(). Returns a decent 128-bit hash for strings | 240 | // A subroutine for CityHash128(). Returns a decent 128-bit hash for strings |
| 241 | // of any length representable in signed long. Based on City and Murmur. | 241 | // of any length representable in signed long. Based on City and Murmur. |
| 242 | static uint128 CityMurmur(const char* s, size_t len, uint128 seed) { | 242 | static uint128 CityMurmur(const char* s, std::size_t len, uint128 seed) { |
| 243 | uint64 a = Uint128Low64(seed); | 243 | uint64 a = Uint128Low64(seed); |
| 244 | uint64 b = Uint128High64(seed); | 244 | uint64 b = Uint128High64(seed); |
| 245 | uint64 c = 0; | 245 | uint64 c = 0; |
| @@ -269,7 +269,7 @@ static uint128 CityMurmur(const char* s, size_t len, uint128 seed) { | |||
| 269 | return uint128(a ^ b, HashLen16(b, a)); | 269 | return uint128(a ^ b, HashLen16(b, a)); |
| 270 | } | 270 | } |
| 271 | 271 | ||
| 272 | uint128 CityHash128WithSeed(const char* s, size_t len, uint128 seed) { | 272 | uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed) { |
| 273 | if (len < 128) { | 273 | if (len < 128) { |
| 274 | return CityMurmur(s, len, seed); | 274 | return CityMurmur(s, len, seed); |
| 275 | } | 275 | } |
| @@ -313,7 +313,7 @@ uint128 CityHash128WithSeed(const char* s, size_t len, uint128 seed) { | |||
| 313 | w.first *= 9; | 313 | w.first *= 9; |
| 314 | v.first *= k0; | 314 | v.first *= k0; |
| 315 | // If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s. | 315 | // If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s. |
| 316 | for (size_t tail_done = 0; tail_done < len;) { | 316 | for (std::size_t tail_done = 0; tail_done < len;) { |
| 317 | tail_done += 32; | 317 | tail_done += 32; |
| 318 | y = Rotate(x + y, 42) * k0 + v.second; | 318 | y = Rotate(x + y, 42) * k0 + v.second; |
| 319 | w.first += Fetch64(s + len - tail_done + 16); | 319 | w.first += Fetch64(s + len - tail_done + 16); |
| @@ -331,7 +331,7 @@ uint128 CityHash128WithSeed(const char* s, size_t len, uint128 seed) { | |||
| 331 | return uint128(HashLen16(x + v.second, w.second) + y, HashLen16(x + w.second, y + v.second)); | 331 | return uint128(HashLen16(x + v.second, w.second) + y, HashLen16(x + w.second, y + v.second)); |
| 332 | } | 332 | } |
| 333 | 333 | ||
| 334 | uint128 CityHash128(const char* s, size_t len) { | 334 | uint128 CityHash128(const char* s, std::size_t len) { |
| 335 | return len >= 16 | 335 | return len >= 16 |
| 336 | ? CityHash128WithSeed(s + 16, len - 16, uint128(Fetch64(s), Fetch64(s + 8) + k0)) | 336 | ? CityHash128WithSeed(s + 16, len - 16, uint128(Fetch64(s), Fetch64(s + 8) + k0)) |
| 337 | : CityHash128WithSeed(s, len, uint128(k0, k1)); | 337 | : CityHash128WithSeed(s, len, uint128(k0, k1)); |
diff --git a/src/common/cityhash.h b/src/common/cityhash.h index bcebdb150..4b94f8e18 100644 --- a/src/common/cityhash.h +++ b/src/common/cityhash.h | |||
| @@ -63,7 +63,7 @@ | |||
| 63 | 63 | ||
| 64 | #include <utility> | 64 | #include <utility> |
| 65 | #include <stdint.h> | 65 | #include <stdint.h> |
| 66 | #include <stdlib.h> // for size_t. | 66 | #include <stdlib.h> // for std::size_t. |
| 67 | 67 | ||
| 68 | namespace Common { | 68 | namespace Common { |
| 69 | 69 | ||
| @@ -77,22 +77,22 @@ inline uint64_t Uint128High64(const uint128& x) { | |||
| 77 | } | 77 | } |
| 78 | 78 | ||
| 79 | // Hash function for a byte array. | 79 | // Hash function for a byte array. |
| 80 | uint64_t CityHash64(const char* buf, size_t len); | 80 | uint64_t CityHash64(const char* buf, std::size_t len); |
| 81 | 81 | ||
| 82 | // Hash function for a byte array. For convenience, a 64-bit seed is also | 82 | // Hash function for a byte array. For convenience, a 64-bit seed is also |
| 83 | // hashed into the result. | 83 | // hashed into the result. |
| 84 | uint64_t CityHash64WithSeed(const char* buf, size_t len, uint64_t seed); | 84 | uint64_t CityHash64WithSeed(const char* buf, std::size_t len, uint64_t seed); |
| 85 | 85 | ||
| 86 | // Hash function for a byte array. For convenience, two seeds are also | 86 | // Hash function for a byte array. For convenience, two seeds are also |
| 87 | // hashed into the result. | 87 | // hashed into the result. |
| 88 | uint64_t CityHash64WithSeeds(const char* buf, size_t len, uint64_t seed0, uint64_t seed1); | 88 | uint64_t CityHash64WithSeeds(const char* buf, std::size_t len, uint64_t seed0, uint64_t seed1); |
| 89 | 89 | ||
| 90 | // Hash function for a byte array. | 90 | // Hash function for a byte array. |
| 91 | uint128 CityHash128(const char* s, size_t len); | 91 | uint128 CityHash128(const char* s, std::size_t len); |
| 92 | 92 | ||
| 93 | // Hash function for a byte array. For convenience, a 128-bit seed is also | 93 | // Hash function for a byte array. For convenience, a 128-bit seed is also |
| 94 | // hashed into the result. | 94 | // hashed into the result. |
| 95 | uint128 CityHash128WithSeed(const char* s, size_t len, uint128 seed); | 95 | uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed); |
| 96 | 96 | ||
| 97 | // Hash 128 input bits down to 64 bits of output. | 97 | // Hash 128 input bits down to 64 bits of output. |
| 98 | // This is intended to be a reasonably good hash function. | 98 | // This is intended to be a reasonably good hash function. |
diff --git a/src/common/common_paths.h b/src/common/common_paths.h index df2ce80b1..4f88de768 100644 --- a/src/common/common_paths.h +++ b/src/common/common_paths.h | |||
| @@ -33,6 +33,8 @@ | |||
| 33 | #define NAND_DIR "nand" | 33 | #define NAND_DIR "nand" |
| 34 | #define SYSDATA_DIR "sysdata" | 34 | #define SYSDATA_DIR "sysdata" |
| 35 | #define KEYS_DIR "keys" | 35 | #define KEYS_DIR "keys" |
| 36 | #define LOAD_DIR "load" | ||
| 37 | #define DUMP_DIR "dump" | ||
| 36 | #define LOG_DIR "log" | 38 | #define LOG_DIR "log" |
| 37 | 39 | ||
| 38 | // Filenames | 40 | // Filenames |
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index baa721481..548463787 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp | |||
| @@ -76,7 +76,7 @@ namespace FileUtil { | |||
| 76 | // Modifies argument. | 76 | // Modifies argument. |
| 77 | static void StripTailDirSlashes(std::string& fname) { | 77 | static void StripTailDirSlashes(std::string& fname) { |
| 78 | if (fname.length() > 1) { | 78 | if (fname.length() > 1) { |
| 79 | size_t i = fname.length(); | 79 | std::size_t i = fname.length(); |
| 80 | while (i > 0 && fname[i - 1] == DIR_SEP_CHR) | 80 | while (i > 0 && fname[i - 1] == DIR_SEP_CHR) |
| 81 | --i; | 81 | --i; |
| 82 | fname.resize(i); | 82 | fname.resize(i); |
| @@ -201,7 +201,7 @@ bool CreateFullPath(const std::string& fullPath) { | |||
| 201 | return true; | 201 | return true; |
| 202 | } | 202 | } |
| 203 | 203 | ||
| 204 | size_t position = 0; | 204 | std::size_t position = 0; |
| 205 | while (true) { | 205 | while (true) { |
| 206 | // Find next sub path | 206 | // Find next sub path |
| 207 | position = fullPath.find(DIR_SEP_CHR, position); | 207 | position = fullPath.find(DIR_SEP_CHR, position); |
| @@ -299,7 +299,7 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename) { | |||
| 299 | std::array<char, 1024> buffer; | 299 | std::array<char, 1024> buffer; |
| 300 | while (!feof(input.get())) { | 300 | while (!feof(input.get())) { |
| 301 | // read input | 301 | // read input |
| 302 | size_t rnum = fread(buffer.data(), sizeof(char), buffer.size(), input.get()); | 302 | std::size_t rnum = fread(buffer.data(), sizeof(char), buffer.size(), input.get()); |
| 303 | if (rnum != buffer.size()) { | 303 | if (rnum != buffer.size()) { |
| 304 | if (ferror(input.get()) != 0) { | 304 | if (ferror(input.get()) != 0) { |
| 305 | LOG_ERROR(Common_Filesystem, "failed reading from source, {} --> {}: {}", | 305 | LOG_ERROR(Common_Filesystem, "failed reading from source, {} --> {}: {}", |
| @@ -309,7 +309,7 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename) { | |||
| 309 | } | 309 | } |
| 310 | 310 | ||
| 311 | // write output | 311 | // write output |
| 312 | size_t wnum = fwrite(buffer.data(), sizeof(char), rnum, output.get()); | 312 | std::size_t wnum = fwrite(buffer.data(), sizeof(char), rnum, output.get()); |
| 313 | if (wnum != rnum) { | 313 | if (wnum != rnum) { |
| 314 | LOG_ERROR(Common_Filesystem, "failed writing to output, {} --> {}: {}", srcFilename, | 314 | LOG_ERROR(Common_Filesystem, "failed writing to output, {} --> {}: {}", srcFilename, |
| 315 | destFilename, GetLastErrorMsg()); | 315 | destFilename, GetLastErrorMsg()); |
| @@ -705,6 +705,8 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) { | |||
| 705 | #endif | 705 | #endif |
| 706 | paths.emplace(UserPath::SDMCDir, user_path + SDMC_DIR DIR_SEP); | 706 | paths.emplace(UserPath::SDMCDir, user_path + SDMC_DIR DIR_SEP); |
| 707 | paths.emplace(UserPath::NANDDir, user_path + NAND_DIR DIR_SEP); | 707 | paths.emplace(UserPath::NANDDir, user_path + NAND_DIR DIR_SEP); |
| 708 | paths.emplace(UserPath::LoadDir, user_path + LOAD_DIR DIR_SEP); | ||
| 709 | paths.emplace(UserPath::DumpDir, user_path + DUMP_DIR DIR_SEP); | ||
| 708 | paths.emplace(UserPath::SysDataDir, user_path + SYSDATA_DIR DIR_SEP); | 710 | paths.emplace(UserPath::SysDataDir, user_path + SYSDATA_DIR DIR_SEP); |
| 709 | paths.emplace(UserPath::KeysDir, user_path + KEYS_DIR DIR_SEP); | 711 | paths.emplace(UserPath::KeysDir, user_path + KEYS_DIR DIR_SEP); |
| 710 | // TODO: Put the logs in a better location for each OS | 712 | // TODO: Put the logs in a better location for each OS |
| @@ -756,11 +758,11 @@ std::string GetNANDRegistrationDir(bool system) { | |||
| 756 | return GetUserPath(UserPath::NANDDir) + "user/Contents/registered/"; | 758 | return GetUserPath(UserPath::NANDDir) + "user/Contents/registered/"; |
| 757 | } | 759 | } |
| 758 | 760 | ||
| 759 | size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename) { | 761 | std::size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename) { |
| 760 | return FileUtil::IOFile(filename, text_file ? "w" : "wb").WriteBytes(str.data(), str.size()); | 762 | return FileUtil::IOFile(filename, text_file ? "w" : "wb").WriteBytes(str.data(), str.size()); |
| 761 | } | 763 | } |
| 762 | 764 | ||
| 763 | size_t ReadFileToString(bool text_file, const char* filename, std::string& str) { | 765 | std::size_t ReadFileToString(bool text_file, const char* filename, std::string& str) { |
| 764 | IOFile file(filename, text_file ? "r" : "rb"); | 766 | IOFile file(filename, text_file ? "r" : "rb"); |
| 765 | 767 | ||
| 766 | if (!file.IsOpen()) | 768 | if (!file.IsOpen()) |
| @@ -829,7 +831,7 @@ std::vector<std::string> SplitPathComponents(std::string_view filename) { | |||
| 829 | std::string_view GetParentPath(std::string_view path) { | 831 | std::string_view GetParentPath(std::string_view path) { |
| 830 | const auto name_bck_index = path.rfind('\\'); | 832 | const auto name_bck_index = path.rfind('\\'); |
| 831 | const auto name_fwd_index = path.rfind('/'); | 833 | const auto name_fwd_index = path.rfind('/'); |
| 832 | size_t name_index; | 834 | std::size_t name_index; |
| 833 | 835 | ||
| 834 | if (name_bck_index == std::string_view::npos || name_fwd_index == std::string_view::npos) { | 836 | if (name_bck_index == std::string_view::npos || name_fwd_index == std::string_view::npos) { |
| 835 | name_index = std::min(name_bck_index, name_fwd_index); | 837 | name_index = std::min(name_bck_index, name_fwd_index); |
| @@ -868,7 +870,7 @@ std::string_view GetFilename(std::string_view path) { | |||
| 868 | } | 870 | } |
| 869 | 871 | ||
| 870 | std::string_view GetExtensionFromFilename(std::string_view name) { | 872 | std::string_view GetExtensionFromFilename(std::string_view name) { |
| 871 | const size_t index = name.rfind('.'); | 873 | const std::size_t index = name.rfind('.'); |
| 872 | 874 | ||
| 873 | if (index == std::string_view::npos) { | 875 | if (index == std::string_view::npos) { |
| 874 | return {}; | 876 | return {}; |
diff --git a/src/common/file_util.h b/src/common/file_util.h index 2f13d0b6b..3d8fe6264 100644 --- a/src/common/file_util.h +++ b/src/common/file_util.h | |||
| @@ -29,6 +29,8 @@ enum class UserPath { | |||
| 29 | NANDDir, | 29 | NANDDir, |
| 30 | RootDir, | 30 | RootDir, |
| 31 | SDMCDir, | 31 | SDMCDir, |
| 32 | LoadDir, | ||
| 33 | DumpDir, | ||
| 32 | SysDataDir, | 34 | SysDataDir, |
| 33 | UserDir, | 35 | UserDir, |
| 34 | }; | 36 | }; |
| @@ -143,8 +145,9 @@ const std::string& GetExeDirectory(); | |||
| 143 | std::string AppDataRoamingDirectory(); | 145 | std::string AppDataRoamingDirectory(); |
| 144 | #endif | 146 | #endif |
| 145 | 147 | ||
| 146 | size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename); | 148 | std::size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename); |
| 147 | size_t ReadFileToString(bool text_file, const char* filename, std::string& str); | 149 | |
| 150 | std::size_t ReadFileToString(bool text_file, const char* filename, std::string& str); | ||
| 148 | 151 | ||
| 149 | /** | 152 | /** |
| 150 | * Splits the filename into 8.3 format | 153 | * Splits the filename into 8.3 format |
| @@ -177,10 +180,10 @@ std::string_view RemoveTrailingSlash(std::string_view path); | |||
| 177 | 180 | ||
| 178 | // Creates a new vector containing indices [first, last) from the original. | 181 | // Creates a new vector containing indices [first, last) from the original. |
| 179 | template <typename T> | 182 | template <typename T> |
| 180 | std::vector<T> SliceVector(const std::vector<T>& vector, size_t first, size_t last) { | 183 | std::vector<T> SliceVector(const std::vector<T>& vector, std::size_t first, std::size_t last) { |
| 181 | if (first >= last) | 184 | if (first >= last) |
| 182 | return {}; | 185 | return {}; |
| 183 | last = std::min<size_t>(last, vector.size()); | 186 | last = std::min<std::size_t>(last, vector.size()); |
| 184 | return std::vector<T>(vector.begin() + first, vector.begin() + first + last); | 187 | return std::vector<T>(vector.begin() + first, vector.begin() + first + last); |
| 185 | } | 188 | } |
| 186 | 189 | ||
| @@ -213,47 +216,47 @@ public: | |||
| 213 | bool Close(); | 216 | bool Close(); |
| 214 | 217 | ||
| 215 | template <typename T> | 218 | template <typename T> |
| 216 | size_t ReadArray(T* data, size_t length) const { | 219 | std::size_t ReadArray(T* data, std::size_t length) const { |
| 217 | static_assert(std::is_trivially_copyable_v<T>, | 220 | static_assert(std::is_trivially_copyable_v<T>, |
| 218 | "Given array does not consist of trivially copyable objects"); | 221 | "Given array does not consist of trivially copyable objects"); |
| 219 | 222 | ||
| 220 | if (!IsOpen()) { | 223 | if (!IsOpen()) { |
| 221 | return std::numeric_limits<size_t>::max(); | 224 | return std::numeric_limits<std::size_t>::max(); |
| 222 | } | 225 | } |
| 223 | 226 | ||
| 224 | return std::fread(data, sizeof(T), length, m_file); | 227 | return std::fread(data, sizeof(T), length, m_file); |
| 225 | } | 228 | } |
| 226 | 229 | ||
| 227 | template <typename T> | 230 | template <typename T> |
| 228 | size_t WriteArray(const T* data, size_t length) { | 231 | std::size_t WriteArray(const T* data, std::size_t length) { |
| 229 | static_assert(std::is_trivially_copyable_v<T>, | 232 | static_assert(std::is_trivially_copyable_v<T>, |
| 230 | "Given array does not consist of trivially copyable objects"); | 233 | "Given array does not consist of trivially copyable objects"); |
| 231 | if (!IsOpen()) { | 234 | if (!IsOpen()) { |
| 232 | return std::numeric_limits<size_t>::max(); | 235 | return std::numeric_limits<std::size_t>::max(); |
| 233 | } | 236 | } |
| 234 | 237 | ||
| 235 | return std::fwrite(data, sizeof(T), length, m_file); | 238 | return std::fwrite(data, sizeof(T), length, m_file); |
| 236 | } | 239 | } |
| 237 | 240 | ||
| 238 | template <typename T> | 241 | template <typename T> |
| 239 | size_t ReadBytes(T* data, size_t length) const { | 242 | std::size_t ReadBytes(T* data, std::size_t length) const { |
| 240 | static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable"); | 243 | static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable"); |
| 241 | return ReadArray(reinterpret_cast<char*>(data), length); | 244 | return ReadArray(reinterpret_cast<char*>(data), length); |
| 242 | } | 245 | } |
| 243 | 246 | ||
| 244 | template <typename T> | 247 | template <typename T> |
| 245 | size_t WriteBytes(const T* data, size_t length) { | 248 | std::size_t WriteBytes(const T* data, std::size_t length) { |
| 246 | static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable"); | 249 | static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable"); |
| 247 | return WriteArray(reinterpret_cast<const char*>(data), length); | 250 | return WriteArray(reinterpret_cast<const char*>(data), length); |
| 248 | } | 251 | } |
| 249 | 252 | ||
| 250 | template <typename T> | 253 | template <typename T> |
| 251 | size_t WriteObject(const T& object) { | 254 | std::size_t WriteObject(const T& object) { |
| 252 | static_assert(!std::is_pointer_v<T>, "WriteObject arguments must not be a pointer"); | 255 | static_assert(!std::is_pointer_v<T>, "WriteObject arguments must not be a pointer"); |
| 253 | return WriteArray(&object, 1); | 256 | return WriteArray(&object, 1); |
| 254 | } | 257 | } |
| 255 | 258 | ||
| 256 | size_t WriteString(const std::string& str) { | 259 | std::size_t WriteString(const std::string& str) { |
| 257 | return WriteArray(str.c_str(), str.length()); | 260 | return WriteArray(str.c_str(), str.length()); |
| 258 | } | 261 | } |
| 259 | 262 | ||
diff --git a/src/common/hash.h b/src/common/hash.h index 2c761e545..40194d1ee 100644 --- a/src/common/hash.h +++ b/src/common/hash.h | |||
| @@ -17,7 +17,7 @@ namespace Common { | |||
| 17 | * @param len Length of data (in bytes) to compute hash over | 17 | * @param len Length of data (in bytes) to compute hash over |
| 18 | * @returns 64-bit hash value that was computed over the data block | 18 | * @returns 64-bit hash value that was computed over the data block |
| 19 | */ | 19 | */ |
| 20 | static inline u64 ComputeHash64(const void* data, size_t len) { | 20 | static inline u64 ComputeHash64(const void* data, std::size_t len) { |
| 21 | return CityHash64(static_cast<const char*>(data), len); | 21 | return CityHash64(static_cast<const char*>(data), len); |
| 22 | } | 22 | } |
| 23 | 23 | ||
| @@ -63,7 +63,7 @@ struct HashableStruct { | |||
| 63 | return !(*this == o); | 63 | return !(*this == o); |
| 64 | }; | 64 | }; |
| 65 | 65 | ||
| 66 | size_t Hash() const { | 66 | std::size_t Hash() const { |
| 67 | return Common::ComputeStructHash64(state); | 67 | return Common::ComputeStructHash64(state); |
| 68 | } | 68 | } |
| 69 | }; | 69 | }; |
diff --git a/src/common/hex_util.cpp b/src/common/hex_util.cpp index 8e0a9e46f..589ae5cbf 100644 --- a/src/common/hex_util.cpp +++ b/src/common/hex_util.cpp | |||
| @@ -18,7 +18,7 @@ u8 ToHexNibble(char c1) { | |||
| 18 | return 0; | 18 | return 0; |
| 19 | } | 19 | } |
| 20 | 20 | ||
| 21 | std::array<u8, 16> operator""_array16(const char* str, size_t len) { | 21 | std::array<u8, 16> operator""_array16(const char* str, std::size_t len) { |
| 22 | if (len != 32) { | 22 | if (len != 32) { |
| 23 | LOG_ERROR(Common, | 23 | LOG_ERROR(Common, |
| 24 | "Attempting to parse string to array that is not of correct size (expected=32, " | 24 | "Attempting to parse string to array that is not of correct size (expected=32, " |
| @@ -29,7 +29,7 @@ std::array<u8, 16> operator""_array16(const char* str, size_t len) { | |||
| 29 | return HexStringToArray<16>(str); | 29 | return HexStringToArray<16>(str); |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | std::array<u8, 32> operator""_array32(const char* str, size_t len) { | 32 | std::array<u8, 32> operator""_array32(const char* str, std::size_t len) { |
| 33 | if (len != 64) { | 33 | if (len != 64) { |
| 34 | LOG_ERROR(Common, | 34 | LOG_ERROR(Common, |
| 35 | "Attempting to parse string to array that is not of correct size (expected=64, " | 35 | "Attempting to parse string to array that is not of correct size (expected=64, " |
diff --git a/src/common/hex_util.h b/src/common/hex_util.h index 5fb79bb72..863a5ccd9 100644 --- a/src/common/hex_util.h +++ b/src/common/hex_util.h | |||
| @@ -14,20 +14,20 @@ namespace Common { | |||
| 14 | 14 | ||
| 15 | u8 ToHexNibble(char c1); | 15 | u8 ToHexNibble(char c1); |
| 16 | 16 | ||
| 17 | template <size_t Size, bool le = false> | 17 | template <std::size_t Size, bool le = false> |
| 18 | std::array<u8, Size> HexStringToArray(std::string_view str) { | 18 | std::array<u8, Size> HexStringToArray(std::string_view str) { |
| 19 | std::array<u8, Size> out{}; | 19 | std::array<u8, Size> out{}; |
| 20 | if constexpr (le) { | 20 | if constexpr (le) { |
| 21 | for (size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2) | 21 | for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2) |
| 22 | out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]); | 22 | out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]); |
| 23 | } else { | 23 | } else { |
| 24 | for (size_t i = 0; i < 2 * Size; i += 2) | 24 | for (std::size_t i = 0; i < 2 * Size; i += 2) |
| 25 | out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]); | 25 | out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]); |
| 26 | } | 26 | } |
| 27 | return out; | 27 | return out; |
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | template <size_t Size> | 30 | template <std::size_t Size> |
| 31 | std::string HexArrayToString(std::array<u8, Size> array, bool upper = true) { | 31 | std::string HexArrayToString(std::array<u8, Size> array, bool upper = true) { |
| 32 | std::string out; | 32 | std::string out; |
| 33 | for (u8 c : array) | 33 | for (u8 c : array) |
| @@ -35,7 +35,7 @@ std::string HexArrayToString(std::array<u8, Size> array, bool upper = true) { | |||
| 35 | return out; | 35 | return out; |
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | std::array<u8, 0x10> operator"" _array16(const char* str, size_t len); | 38 | std::array<u8, 0x10> operator"" _array16(const char* str, std::size_t len); |
| 39 | std::array<u8, 0x20> operator"" _array32(const char* str, size_t len); | 39 | std::array<u8, 0x20> operator"" _array32(const char* str, std::size_t len); |
| 40 | 40 | ||
| 41 | } // namespace Common | 41 | } // namespace Common |
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 1323f8d0f..9f5918851 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp | |||
| @@ -135,7 +135,7 @@ FileBackend::FileBackend(const std::string& filename) | |||
| 135 | void FileBackend::Write(const Entry& entry) { | 135 | void FileBackend::Write(const Entry& entry) { |
| 136 | // prevent logs from going over the maximum size (in case its spamming and the user doesn't | 136 | // prevent logs from going over the maximum size (in case its spamming and the user doesn't |
| 137 | // know) | 137 | // know) |
| 138 | constexpr size_t MAX_BYTES_WRITTEN = 50 * 1024L * 1024L; | 138 | constexpr std::size_t MAX_BYTES_WRITTEN = 50 * 1024L * 1024L; |
| 139 | if (!file.IsOpen() || bytes_written > MAX_BYTES_WRITTEN) { | 139 | if (!file.IsOpen() || bytes_written > MAX_BYTES_WRITTEN) { |
| 140 | return; | 140 | return; |
| 141 | } | 141 | } |
| @@ -183,6 +183,7 @@ void FileBackend::Write(const Entry& entry) { | |||
| 183 | SUB(Service, FS) \ | 183 | SUB(Service, FS) \ |
| 184 | SUB(Service, GRC) \ | 184 | SUB(Service, GRC) \ |
| 185 | SUB(Service, HID) \ | 185 | SUB(Service, HID) \ |
| 186 | SUB(Service, IRS) \ | ||
| 186 | SUB(Service, LBL) \ | 187 | SUB(Service, LBL) \ |
| 187 | SUB(Service, LDN) \ | 188 | SUB(Service, LDN) \ |
| 188 | SUB(Service, LDR) \ | 189 | SUB(Service, LDR) \ |
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h index b3f4b9cef..11edbf1b6 100644 --- a/src/common/logging/backend.h +++ b/src/common/logging/backend.h | |||
| @@ -100,7 +100,7 @@ public: | |||
| 100 | 100 | ||
| 101 | private: | 101 | private: |
| 102 | FileUtil::IOFile file; | 102 | FileUtil::IOFile file; |
| 103 | size_t bytes_written; | 103 | std::size_t bytes_written; |
| 104 | }; | 104 | }; |
| 105 | 105 | ||
| 106 | void AddBackend(std::unique_ptr<Backend> backend); | 106 | void AddBackend(std::unique_ptr<Backend> backend); |
diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index 2dd331152..2eccbcd8d 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp | |||
| @@ -71,7 +71,7 @@ void Filter::ResetAll(Level level) { | |||
| 71 | } | 71 | } |
| 72 | 72 | ||
| 73 | void Filter::SetClassLevel(Class log_class, Level level) { | 73 | void Filter::SetClassLevel(Class log_class, Level level) { |
| 74 | class_levels[static_cast<size_t>(log_class)] = level; | 74 | class_levels[static_cast<std::size_t>(log_class)] = level; |
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | void Filter::ParseFilterString(std::string_view filter_view) { | 77 | void Filter::ParseFilterString(std::string_view filter_view) { |
| @@ -93,7 +93,8 @@ void Filter::ParseFilterString(std::string_view filter_view) { | |||
| 93 | } | 93 | } |
| 94 | 94 | ||
| 95 | bool Filter::CheckMessage(Class log_class, Level level) const { | 95 | bool Filter::CheckMessage(Class log_class, Level level) const { |
| 96 | return static_cast<u8>(level) >= static_cast<u8>(class_levels[static_cast<size_t>(log_class)]); | 96 | return static_cast<u8>(level) >= |
| 97 | static_cast<u8>(class_levels[static_cast<std::size_t>(log_class)]); | ||
| 97 | } | 98 | } |
| 98 | 99 | ||
| 99 | bool Filter::IsDebug() const { | 100 | bool Filter::IsDebug() const { |
diff --git a/src/common/logging/filter.h b/src/common/logging/filter.h index f7e3b87c9..773df6f2c 100644 --- a/src/common/logging/filter.h +++ b/src/common/logging/filter.h | |||
| @@ -49,6 +49,6 @@ public: | |||
| 49 | bool IsDebug() const; | 49 | bool IsDebug() const; |
| 50 | 50 | ||
| 51 | private: | 51 | private: |
| 52 | std::array<Level, static_cast<size_t>(Class::Count)> class_levels; | 52 | std::array<Level, static_cast<std::size_t>(Class::Count)> class_levels; |
| 53 | }; | 53 | }; |
| 54 | } // namespace Log | 54 | } // namespace Log |
diff --git a/src/common/logging/log.h b/src/common/logging/log.h index 4d577524f..abbd056ee 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h | |||
| @@ -70,6 +70,7 @@ enum class Class : ClassType { | |||
| 70 | Service_FS, ///< The FS (Filesystem) service | 70 | Service_FS, ///< The FS (Filesystem) service |
| 71 | Service_GRC, ///< The game recording service | 71 | Service_GRC, ///< The game recording service |
| 72 | Service_HID, ///< The HID (Human interface device) service | 72 | Service_HID, ///< The HID (Human interface device) service |
| 73 | Service_IRS, ///< The IRS service | ||
| 73 | Service_LBL, ///< The LBL (LCD backlight) service | 74 | Service_LBL, ///< The LBL (LCD backlight) service |
| 74 | Service_LDN, ///< The LDN (Local domain network) service | 75 | Service_LDN, ///< The LDN (Local domain network) service |
| 75 | Service_LDR, ///< The loader service | 76 | Service_LDR, ///< The loader service |
diff --git a/src/common/memory_util.cpp b/src/common/memory_util.cpp index 09462ccee..9736fb12a 100644 --- a/src/common/memory_util.cpp +++ b/src/common/memory_util.cpp | |||
| @@ -25,7 +25,7 @@ | |||
| 25 | // This is purposely not a full wrapper for virtualalloc/mmap, but it | 25 | // This is purposely not a full wrapper for virtualalloc/mmap, but it |
| 26 | // provides exactly the primitive operations that Dolphin needs. | 26 | // provides exactly the primitive operations that Dolphin needs. |
| 27 | 27 | ||
| 28 | void* AllocateExecutableMemory(size_t size, bool low) { | 28 | void* AllocateExecutableMemory(std::size_t size, bool low) { |
| 29 | #if defined(_WIN32) | 29 | #if defined(_WIN32) |
| 30 | void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); | 30 | void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); |
| 31 | #else | 31 | #else |
| @@ -74,7 +74,7 @@ void* AllocateExecutableMemory(size_t size, bool low) { | |||
| 74 | return ptr; | 74 | return ptr; |
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | void* AllocateMemoryPages(size_t size) { | 77 | void* AllocateMemoryPages(std::size_t size) { |
| 78 | #ifdef _WIN32 | 78 | #ifdef _WIN32 |
| 79 | void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE); | 79 | void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE); |
| 80 | #else | 80 | #else |
| @@ -90,7 +90,7 @@ void* AllocateMemoryPages(size_t size) { | |||
| 90 | return ptr; | 90 | return ptr; |
| 91 | } | 91 | } |
| 92 | 92 | ||
| 93 | void* AllocateAlignedMemory(size_t size, size_t alignment) { | 93 | void* AllocateAlignedMemory(std::size_t size, std::size_t alignment) { |
| 94 | #ifdef _WIN32 | 94 | #ifdef _WIN32 |
| 95 | void* ptr = _aligned_malloc(size, alignment); | 95 | void* ptr = _aligned_malloc(size, alignment); |
| 96 | #else | 96 | #else |
| @@ -109,7 +109,7 @@ void* AllocateAlignedMemory(size_t size, size_t alignment) { | |||
| 109 | return ptr; | 109 | return ptr; |
| 110 | } | 110 | } |
| 111 | 111 | ||
| 112 | void FreeMemoryPages(void* ptr, size_t size) { | 112 | void FreeMemoryPages(void* ptr, std::size_t size) { |
| 113 | if (ptr) { | 113 | if (ptr) { |
| 114 | #ifdef _WIN32 | 114 | #ifdef _WIN32 |
| 115 | if (!VirtualFree(ptr, 0, MEM_RELEASE)) | 115 | if (!VirtualFree(ptr, 0, MEM_RELEASE)) |
| @@ -130,7 +130,7 @@ void FreeAlignedMemory(void* ptr) { | |||
| 130 | } | 130 | } |
| 131 | } | 131 | } |
| 132 | 132 | ||
| 133 | void WriteProtectMemory(void* ptr, size_t size, bool allowExecute) { | 133 | void WriteProtectMemory(void* ptr, std::size_t size, bool allowExecute) { |
| 134 | #ifdef _WIN32 | 134 | #ifdef _WIN32 |
| 135 | DWORD oldValue; | 135 | DWORD oldValue; |
| 136 | if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READ : PAGE_READONLY, &oldValue)) | 136 | if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READ : PAGE_READONLY, &oldValue)) |
| @@ -140,7 +140,7 @@ void WriteProtectMemory(void* ptr, size_t size, bool allowExecute) { | |||
| 140 | #endif | 140 | #endif |
| 141 | } | 141 | } |
| 142 | 142 | ||
| 143 | void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute) { | 143 | void UnWriteProtectMemory(void* ptr, std::size_t size, bool allowExecute) { |
| 144 | #ifdef _WIN32 | 144 | #ifdef _WIN32 |
| 145 | DWORD oldValue; | 145 | DWORD oldValue; |
| 146 | if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE, | 146 | if (!VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE, |
diff --git a/src/common/memory_util.h b/src/common/memory_util.h index 76ca5a30c..aad071979 100644 --- a/src/common/memory_util.h +++ b/src/common/memory_util.h | |||
| @@ -7,13 +7,13 @@ | |||
| 7 | #include <cstddef> | 7 | #include <cstddef> |
| 8 | #include <string> | 8 | #include <string> |
| 9 | 9 | ||
| 10 | void* AllocateExecutableMemory(size_t size, bool low = true); | 10 | void* AllocateExecutableMemory(std::size_t size, bool low = true); |
| 11 | void* AllocateMemoryPages(size_t size); | 11 | void* AllocateMemoryPages(std::size_t size); |
| 12 | void FreeMemoryPages(void* ptr, size_t size); | 12 | void FreeMemoryPages(void* ptr, std::size_t size); |
| 13 | void* AllocateAlignedMemory(size_t size, size_t alignment); | 13 | void* AllocateAlignedMemory(std::size_t size, std::size_t alignment); |
| 14 | void FreeAlignedMemory(void* ptr); | 14 | void FreeAlignedMemory(void* ptr); |
| 15 | void WriteProtectMemory(void* ptr, size_t size, bool executable = false); | 15 | void WriteProtectMemory(void* ptr, std::size_t size, bool executable = false); |
| 16 | void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute = false); | 16 | void UnWriteProtectMemory(void* ptr, std::size_t size, bool allowExecute = false); |
| 17 | std::string MemUsage(); | 17 | std::string MemUsage(); |
| 18 | 18 | ||
| 19 | inline int GetPageSize() { | 19 | inline int GetPageSize() { |
diff --git a/src/common/misc.cpp b/src/common/misc.cpp index 3fa8a3bc4..68cb86cd1 100644 --- a/src/common/misc.cpp +++ b/src/common/misc.cpp | |||
| @@ -16,7 +16,7 @@ | |||
| 16 | // Call directly after the command or use the error num. | 16 | // Call directly after the command or use the error num. |
| 17 | // This function might change the error code. | 17 | // This function might change the error code. |
| 18 | std::string GetLastErrorMsg() { | 18 | std::string GetLastErrorMsg() { |
| 19 | static const size_t buff_size = 255; | 19 | static const std::size_t buff_size = 255; |
| 20 | char err_str[buff_size]; | 20 | char err_str[buff_size]; |
| 21 | 21 | ||
| 22 | #ifdef _WIN32 | 22 | #ifdef _WIN32 |
diff --git a/src/common/ring_buffer.h b/src/common/ring_buffer.h index 30d934a38..abe3b4dc2 100644 --- a/src/common/ring_buffer.h +++ b/src/common/ring_buffer.h | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include <atomic> | 9 | #include <atomic> |
| 10 | #include <cstddef> | 10 | #include <cstddef> |
| 11 | #include <cstring> | 11 | #include <cstring> |
| 12 | #include <new> | ||
| 12 | #include <type_traits> | 13 | #include <type_traits> |
| 13 | #include <vector> | 14 | #include <vector> |
| 14 | #include "common/common_types.h" | 15 | #include "common/common_types.h" |
| @@ -19,31 +20,31 @@ namespace Common { | |||
| 19 | /// @tparam T Element type | 20 | /// @tparam T Element type |
| 20 | /// @tparam capacity Number of slots in ring buffer | 21 | /// @tparam capacity Number of slots in ring buffer |
| 21 | /// @tparam granularity Slot size in terms of number of elements | 22 | /// @tparam granularity Slot size in terms of number of elements |
| 22 | template <typename T, size_t capacity, size_t granularity = 1> | 23 | template <typename T, std::size_t capacity, std::size_t granularity = 1> |
| 23 | class RingBuffer { | 24 | class RingBuffer { |
| 24 | /// A "slot" is made of `granularity` elements of `T`. | 25 | /// A "slot" is made of `granularity` elements of `T`. |
| 25 | static constexpr size_t slot_size = granularity * sizeof(T); | 26 | static constexpr std::size_t slot_size = granularity * sizeof(T); |
| 26 | // T must be safely memcpy-able and have a trivial default constructor. | 27 | // T must be safely memcpy-able and have a trivial default constructor. |
| 27 | static_assert(std::is_trivial_v<T>); | 28 | static_assert(std::is_trivial_v<T>); |
| 28 | // Ensure capacity is sensible. | 29 | // Ensure capacity is sensible. |
| 29 | static_assert(capacity < std::numeric_limits<size_t>::max() / 2 / granularity); | 30 | static_assert(capacity < std::numeric_limits<std::size_t>::max() / 2 / granularity); |
| 30 | static_assert((capacity & (capacity - 1)) == 0, "capacity must be a power of two"); | 31 | static_assert((capacity & (capacity - 1)) == 0, "capacity must be a power of two"); |
| 31 | // Ensure lock-free. | 32 | // Ensure lock-free. |
| 32 | static_assert(std::atomic<size_t>::is_always_lock_free); | 33 | static_assert(std::atomic_size_t::is_always_lock_free); |
| 33 | 34 | ||
| 34 | public: | 35 | public: |
| 35 | /// Pushes slots into the ring buffer | 36 | /// Pushes slots into the ring buffer |
| 36 | /// @param new_slots Pointer to the slots to push | 37 | /// @param new_slots Pointer to the slots to push |
| 37 | /// @param slot_count Number of slots to push | 38 | /// @param slot_count Number of slots to push |
| 38 | /// @returns The number of slots actually pushed | 39 | /// @returns The number of slots actually pushed |
| 39 | size_t Push(const void* new_slots, size_t slot_count) { | 40 | std::size_t Push(const void* new_slots, std::size_t slot_count) { |
| 40 | const size_t write_index = m_write_index.load(); | 41 | const std::size_t write_index = m_write_index.load(); |
| 41 | const size_t slots_free = capacity + m_read_index.load() - write_index; | 42 | const std::size_t slots_free = capacity + m_read_index.load() - write_index; |
| 42 | const size_t push_count = std::min(slot_count, slots_free); | 43 | const std::size_t push_count = std::min(slot_count, slots_free); |
| 43 | 44 | ||
| 44 | const size_t pos = write_index % capacity; | 45 | const std::size_t pos = write_index % capacity; |
| 45 | const size_t first_copy = std::min(capacity - pos, push_count); | 46 | const std::size_t first_copy = std::min(capacity - pos, push_count); |
| 46 | const size_t second_copy = push_count - first_copy; | 47 | const std::size_t second_copy = push_count - first_copy; |
| 47 | 48 | ||
| 48 | const char* in = static_cast<const char*>(new_slots); | 49 | const char* in = static_cast<const char*>(new_slots); |
| 49 | std::memcpy(m_data.data() + pos * granularity, in, first_copy * slot_size); | 50 | std::memcpy(m_data.data() + pos * granularity, in, first_copy * slot_size); |
| @@ -55,7 +56,7 @@ public: | |||
| 55 | return push_count; | 56 | return push_count; |
| 56 | } | 57 | } |
| 57 | 58 | ||
| 58 | size_t Push(const std::vector<T>& input) { | 59 | std::size_t Push(const std::vector<T>& input) { |
| 59 | return Push(input.data(), input.size()); | 60 | return Push(input.data(), input.size()); |
| 60 | } | 61 | } |
| 61 | 62 | ||
| @@ -63,14 +64,14 @@ public: | |||
| 63 | /// @param output Where to store the popped slots | 64 | /// @param output Where to store the popped slots |
| 64 | /// @param max_slots Maximum number of slots to pop | 65 | /// @param max_slots Maximum number of slots to pop |
| 65 | /// @returns The number of slots actually popped | 66 | /// @returns The number of slots actually popped |
| 66 | size_t Pop(void* output, size_t max_slots = ~size_t(0)) { | 67 | std::size_t Pop(void* output, std::size_t max_slots = ~std::size_t(0)) { |
| 67 | const size_t read_index = m_read_index.load(); | 68 | const std::size_t read_index = m_read_index.load(); |
| 68 | const size_t slots_filled = m_write_index.load() - read_index; | 69 | const std::size_t slots_filled = m_write_index.load() - read_index; |
| 69 | const size_t pop_count = std::min(slots_filled, max_slots); | 70 | const std::size_t pop_count = std::min(slots_filled, max_slots); |
| 70 | 71 | ||
| 71 | const size_t pos = read_index % capacity; | 72 | const std::size_t pos = read_index % capacity; |
| 72 | const size_t first_copy = std::min(capacity - pos, pop_count); | 73 | const std::size_t first_copy = std::min(capacity - pos, pop_count); |
| 73 | const size_t second_copy = pop_count - first_copy; | 74 | const std::size_t second_copy = pop_count - first_copy; |
| 74 | 75 | ||
| 75 | char* out = static_cast<char*>(output); | 76 | char* out = static_cast<char*>(output); |
| 76 | std::memcpy(out, m_data.data() + pos * granularity, first_copy * slot_size); | 77 | std::memcpy(out, m_data.data() + pos * granularity, first_copy * slot_size); |
| @@ -82,28 +83,35 @@ public: | |||
| 82 | return pop_count; | 83 | return pop_count; |
| 83 | } | 84 | } |
| 84 | 85 | ||
| 85 | std::vector<T> Pop(size_t max_slots = ~size_t(0)) { | 86 | std::vector<T> Pop(std::size_t max_slots = ~std::size_t(0)) { |
| 86 | std::vector<T> out(std::min(max_slots, capacity) * granularity); | 87 | std::vector<T> out(std::min(max_slots, capacity) * granularity); |
| 87 | const size_t count = Pop(out.data(), out.size() / granularity); | 88 | const std::size_t count = Pop(out.data(), out.size() / granularity); |
| 88 | out.resize(count * granularity); | 89 | out.resize(count * granularity); |
| 89 | return out; | 90 | return out; |
| 90 | } | 91 | } |
| 91 | 92 | ||
| 92 | /// @returns Number of slots used | 93 | /// @returns Number of slots used |
| 93 | size_t Size() const { | 94 | std::size_t Size() const { |
| 94 | return m_write_index.load() - m_read_index.load(); | 95 | return m_write_index.load() - m_read_index.load(); |
| 95 | } | 96 | } |
| 96 | 97 | ||
| 97 | /// @returns Maximum size of ring buffer | 98 | /// @returns Maximum size of ring buffer |
| 98 | constexpr size_t Capacity() const { | 99 | constexpr std::size_t Capacity() const { |
| 99 | return capacity; | 100 | return capacity; |
| 100 | } | 101 | } |
| 101 | 102 | ||
| 102 | private: | 103 | private: |
| 103 | // It is important to align the below variables for performance reasons: | 104 | // It is important to align the below variables for performance reasons: |
| 104 | // Having them on the same cache-line would result in false-sharing between them. | 105 | // Having them on the same cache-line would result in false-sharing between them. |
| 105 | alignas(128) std::atomic<size_t> m_read_index{0}; | 106 | // TODO: Remove this ifdef whenever clang and GCC support |
| 106 | alignas(128) std::atomic<size_t> m_write_index{0}; | 107 | // std::hardware_destructive_interference_size. |
| 108 | #if defined(_MSC_VER) && _MSC_VER >= 1911 | ||
| 109 | alignas(std::hardware_destructive_interference_size) std::atomic_size_t m_read_index{0}; | ||
| 110 | alignas(std::hardware_destructive_interference_size) std::atomic_size_t m_write_index{0}; | ||
| 111 | #else | ||
| 112 | alignas(128) std::atomic_size_t m_read_index{0}; | ||
| 113 | alignas(128) std::atomic_size_t m_write_index{0}; | ||
| 114 | #endif | ||
| 107 | 115 | ||
| 108 | std::array<T, granularity * capacity> m_data; | 116 | std::array<T, granularity * capacity> m_data; |
| 109 | }; | 117 | }; |
diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp index 0ca663032..c9a5425a7 100644 --- a/src/common/string_util.cpp +++ b/src/common/string_util.cpp | |||
| @@ -37,7 +37,7 @@ std::string ToUpper(std::string str) { | |||
| 37 | } | 37 | } |
| 38 | 38 | ||
| 39 | // For Debugging. Read out an u8 array. | 39 | // For Debugging. Read out an u8 array. |
| 40 | std::string ArrayToString(const u8* data, size_t size, int line_len, bool spaces) { | 40 | std::string ArrayToString(const u8* data, std::size_t size, int line_len, bool spaces) { |
| 41 | std::ostringstream oss; | 41 | std::ostringstream oss; |
| 42 | oss << std::setfill('0') << std::hex; | 42 | oss << std::setfill('0') << std::hex; |
| 43 | 43 | ||
| @@ -60,7 +60,7 @@ std::string StringFromBuffer(const std::vector<u8>& data) { | |||
| 60 | 60 | ||
| 61 | // Turns " hej " into "hej". Also handles tabs. | 61 | // Turns " hej " into "hej". Also handles tabs. |
| 62 | std::string StripSpaces(const std::string& str) { | 62 | std::string StripSpaces(const std::string& str) { |
| 63 | const size_t s = str.find_first_not_of(" \t\r\n"); | 63 | const std::size_t s = str.find_first_not_of(" \t\r\n"); |
| 64 | 64 | ||
| 65 | if (str.npos != s) | 65 | if (str.npos != s) |
| 66 | return str.substr(s, str.find_last_not_of(" \t\r\n") - s + 1); | 66 | return str.substr(s, str.find_last_not_of(" \t\r\n") - s + 1); |
| @@ -121,10 +121,10 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _ | |||
| 121 | if (full_path.empty()) | 121 | if (full_path.empty()) |
| 122 | return false; | 122 | return false; |
| 123 | 123 | ||
| 124 | size_t dir_end = full_path.find_last_of("/" | 124 | std::size_t dir_end = full_path.find_last_of("/" |
| 125 | // windows needs the : included for something like just "C:" to be considered a directory | 125 | // windows needs the : included for something like just "C:" to be considered a directory |
| 126 | #ifdef _WIN32 | 126 | #ifdef _WIN32 |
| 127 | "\\:" | 127 | "\\:" |
| 128 | #endif | 128 | #endif |
| 129 | ); | 129 | ); |
| 130 | if (std::string::npos == dir_end) | 130 | if (std::string::npos == dir_end) |
| @@ -132,7 +132,7 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _ | |||
| 132 | else | 132 | else |
| 133 | dir_end += 1; | 133 | dir_end += 1; |
| 134 | 134 | ||
| 135 | size_t fname_end = full_path.rfind('.'); | 135 | std::size_t fname_end = full_path.rfind('.'); |
| 136 | if (fname_end < dir_end || std::string::npos == fname_end) | 136 | if (fname_end < dir_end || std::string::npos == fname_end) |
| 137 | fname_end = full_path.size(); | 137 | fname_end = full_path.size(); |
| 138 | 138 | ||
| @@ -172,7 +172,7 @@ void SplitString(const std::string& str, const char delim, std::vector<std::stri | |||
| 172 | } | 172 | } |
| 173 | 173 | ||
| 174 | std::string TabsToSpaces(int tab_size, std::string in) { | 174 | std::string TabsToSpaces(int tab_size, std::string in) { |
| 175 | size_t i = 0; | 175 | std::size_t i = 0; |
| 176 | 176 | ||
| 177 | while ((i = in.find('\t')) != std::string::npos) { | 177 | while ((i = in.find('\t')) != std::string::npos) { |
| 178 | in.replace(i, 1, tab_size, ' '); | 178 | in.replace(i, 1, tab_size, ' '); |
| @@ -182,7 +182,7 @@ std::string TabsToSpaces(int tab_size, std::string in) { | |||
| 182 | } | 182 | } |
| 183 | 183 | ||
| 184 | std::string ReplaceAll(std::string result, const std::string& src, const std::string& dest) { | 184 | std::string ReplaceAll(std::string result, const std::string& src, const std::string& dest) { |
| 185 | size_t pos = 0; | 185 | std::size_t pos = 0; |
| 186 | 186 | ||
| 187 | if (src == dest) | 187 | if (src == dest) |
| 188 | return result; | 188 | return result; |
| @@ -280,22 +280,22 @@ static std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>& | |||
| 280 | return {}; | 280 | return {}; |
| 281 | } | 281 | } |
| 282 | 282 | ||
| 283 | const size_t in_bytes = sizeof(T) * input.size(); | 283 | const std::size_t in_bytes = sizeof(T) * input.size(); |
| 284 | // Multiply by 4, which is the max number of bytes to encode a codepoint | 284 | // Multiply by 4, which is the max number of bytes to encode a codepoint |
| 285 | const size_t out_buffer_size = 4 * in_bytes; | 285 | const std::size_t out_buffer_size = 4 * in_bytes; |
| 286 | 286 | ||
| 287 | std::string out_buffer(out_buffer_size, '\0'); | 287 | std::string out_buffer(out_buffer_size, '\0'); |
| 288 | 288 | ||
| 289 | auto src_buffer = &input[0]; | 289 | auto src_buffer = &input[0]; |
| 290 | size_t src_bytes = in_bytes; | 290 | std::size_t src_bytes = in_bytes; |
| 291 | auto dst_buffer = &out_buffer[0]; | 291 | auto dst_buffer = &out_buffer[0]; |
| 292 | size_t dst_bytes = out_buffer.size(); | 292 | std::size_t dst_bytes = out_buffer.size(); |
| 293 | 293 | ||
| 294 | while (0 != src_bytes) { | 294 | while (0 != src_bytes) { |
| 295 | size_t const iconv_result = | 295 | std::size_t const iconv_result = |
| 296 | iconv(conv_desc, (char**)(&src_buffer), &src_bytes, &dst_buffer, &dst_bytes); | 296 | iconv(conv_desc, (char**)(&src_buffer), &src_bytes, &dst_buffer, &dst_bytes); |
| 297 | 297 | ||
| 298 | if (static_cast<size_t>(-1) == iconv_result) { | 298 | if (static_cast<std::size_t>(-1) == iconv_result) { |
| 299 | if (EILSEQ == errno || EINVAL == errno) { | 299 | if (EILSEQ == errno || EINVAL == errno) { |
| 300 | // Try to skip the bad character | 300 | // Try to skip the bad character |
| 301 | if (0 != src_bytes) { | 301 | if (0 != src_bytes) { |
| @@ -326,22 +326,22 @@ std::u16string UTF8ToUTF16(const std::string& input) { | |||
| 326 | return {}; | 326 | return {}; |
| 327 | } | 327 | } |
| 328 | 328 | ||
| 329 | const size_t in_bytes = sizeof(char) * input.size(); | 329 | const std::size_t in_bytes = sizeof(char) * input.size(); |
| 330 | // Multiply by 4, which is the max number of bytes to encode a codepoint | 330 | // Multiply by 4, which is the max number of bytes to encode a codepoint |
| 331 | const size_t out_buffer_size = 4 * sizeof(char16_t) * in_bytes; | 331 | const std::size_t out_buffer_size = 4 * sizeof(char16_t) * in_bytes; |
| 332 | 332 | ||
| 333 | std::u16string out_buffer(out_buffer_size, char16_t{}); | 333 | std::u16string out_buffer(out_buffer_size, char16_t{}); |
| 334 | 334 | ||
| 335 | char* src_buffer = const_cast<char*>(&input[0]); | 335 | char* src_buffer = const_cast<char*>(&input[0]); |
| 336 | size_t src_bytes = in_bytes; | 336 | std::size_t src_bytes = in_bytes; |
| 337 | char* dst_buffer = (char*)(&out_buffer[0]); | 337 | char* dst_buffer = (char*)(&out_buffer[0]); |
| 338 | size_t dst_bytes = out_buffer.size(); | 338 | std::size_t dst_bytes = out_buffer.size(); |
| 339 | 339 | ||
| 340 | while (0 != src_bytes) { | 340 | while (0 != src_bytes) { |
| 341 | size_t const iconv_result = | 341 | std::size_t const iconv_result = |
| 342 | iconv(conv_desc, &src_buffer, &src_bytes, &dst_buffer, &dst_bytes); | 342 | iconv(conv_desc, &src_buffer, &src_bytes, &dst_buffer, &dst_bytes); |
| 343 | 343 | ||
| 344 | if (static_cast<size_t>(-1) == iconv_result) { | 344 | if (static_cast<std::size_t>(-1) == iconv_result) { |
| 345 | if (EILSEQ == errno || EINVAL == errno) { | 345 | if (EILSEQ == errno || EINVAL == errno) { |
| 346 | // Try to skip the bad character | 346 | // Try to skip the bad character |
| 347 | if (0 != src_bytes) { | 347 | if (0 != src_bytes) { |
| @@ -381,8 +381,8 @@ std::string SHIFTJISToUTF8(const std::string& input) { | |||
| 381 | 381 | ||
| 382 | #endif | 382 | #endif |
| 383 | 383 | ||
| 384 | std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, size_t max_len) { | 384 | std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t max_len) { |
| 385 | size_t len = 0; | 385 | std::size_t len = 0; |
| 386 | while (len < max_len && buffer[len] != '\0') | 386 | while (len < max_len && buffer[len] != '\0') |
| 387 | ++len; | 387 | ++len; |
| 388 | 388 | ||
diff --git a/src/common/string_util.h b/src/common/string_util.h index 4a2143b59..dcca6bc38 100644 --- a/src/common/string_util.h +++ b/src/common/string_util.h | |||
| @@ -19,7 +19,7 @@ std::string ToLower(std::string str); | |||
| 19 | /// Make a string uppercase | 19 | /// Make a string uppercase |
| 20 | std::string ToUpper(std::string str); | 20 | std::string ToUpper(std::string str); |
| 21 | 21 | ||
| 22 | std::string ArrayToString(const u8* data, size_t size, int line_len = 20, bool spaces = true); | 22 | std::string ArrayToString(const u8* data, std::size_t size, int line_len = 20, bool spaces = true); |
| 23 | 23 | ||
| 24 | std::string StringFromBuffer(const std::vector<u8>& data); | 24 | std::string StringFromBuffer(const std::vector<u8>& data); |
| 25 | 25 | ||
| @@ -118,7 +118,7 @@ bool ComparePartialString(InIt begin, InIt end, const char* other) { | |||
| 118 | * Creates a std::string from a fixed-size NUL-terminated char buffer. If the buffer isn't | 118 | * Creates a std::string from a fixed-size NUL-terminated char buffer. If the buffer isn't |
| 119 | * NUL-terminated then the string ends at max_len characters. | 119 | * NUL-terminated then the string ends at max_len characters. |
| 120 | */ | 120 | */ |
| 121 | std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, size_t max_len); | 121 | std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t max_len); |
| 122 | 122 | ||
| 123 | /** | 123 | /** |
| 124 | * Attempts to trim an arbitrary prefix from `path`, leaving only the part starting at `root`. It's | 124 | * Attempts to trim an arbitrary prefix from `path`, leaving only the part starting at `root`. It's |
diff --git a/src/common/thread.h b/src/common/thread.h index 9465e1de7..6cbdb96a3 100644 --- a/src/common/thread.h +++ b/src/common/thread.h | |||
| @@ -60,12 +60,12 @@ private: | |||
| 60 | 60 | ||
| 61 | class Barrier { | 61 | class Barrier { |
| 62 | public: | 62 | public: |
| 63 | explicit Barrier(size_t count_) : count(count_), waiting(0), generation(0) {} | 63 | explicit Barrier(std::size_t count_) : count(count_), waiting(0), generation(0) {} |
| 64 | 64 | ||
| 65 | /// Blocks until all "count" threads have called Sync() | 65 | /// Blocks until all "count" threads have called Sync() |
| 66 | void Sync() { | 66 | void Sync() { |
| 67 | std::unique_lock<std::mutex> lk(mutex); | 67 | std::unique_lock<std::mutex> lk(mutex); |
| 68 | const size_t current_generation = generation; | 68 | const std::size_t current_generation = generation; |
| 69 | 69 | ||
| 70 | if (++waiting == count) { | 70 | if (++waiting == count) { |
| 71 | generation++; | 71 | generation++; |
| @@ -80,21 +80,13 @@ public: | |||
| 80 | private: | 80 | private: |
| 81 | std::condition_variable condvar; | 81 | std::condition_variable condvar; |
| 82 | std::mutex mutex; | 82 | std::mutex mutex; |
| 83 | const size_t count; | 83 | const std::size_t count; |
| 84 | size_t waiting; | 84 | std::size_t waiting; |
| 85 | size_t generation; // Incremented once each time the barrier is used | 85 | std::size_t generation; // Incremented once each time the barrier is used |
| 86 | }; | 86 | }; |
| 87 | 87 | ||
| 88 | void SleepCurrentThread(int ms); | 88 | void SleepCurrentThread(int ms); |
| 89 | void SwitchCurrentThread(); // On Linux, this is equal to sleep 1ms | 89 | void SwitchCurrentThread(); // On Linux, this is equal to sleep 1ms |
| 90 | |||
| 91 | // Use this function during a spin-wait to make the current thread | ||
| 92 | // relax while another thread is working. This may be more efficient | ||
| 93 | // than using events because event functions use kernel calls. | ||
| 94 | inline void YieldCPU() { | ||
| 95 | std::this_thread::yield(); | ||
| 96 | } | ||
| 97 | |||
| 98 | void SetCurrentThreadName(const char* name); | 90 | void SetCurrentThreadName(const char* name); |
| 99 | 91 | ||
| 100 | } // namespace Common | 92 | } // namespace Common |
diff --git a/src/common/x64/xbyak_abi.h b/src/common/x64/xbyak_abi.h index 927da9187..636a5c0f9 100644 --- a/src/common/x64/xbyak_abi.h +++ b/src/common/x64/xbyak_abi.h | |||
| @@ -97,7 +97,7 @@ const BitSet32 ABI_ALL_CALLEE_SAVED = BuildRegSet({ | |||
| 97 | Xbyak::util::xmm15, | 97 | Xbyak::util::xmm15, |
| 98 | }); | 98 | }); |
| 99 | 99 | ||
| 100 | constexpr size_t ABI_SHADOW_SPACE = 0x20; | 100 | constexpr std::size_t ABI_SHADOW_SPACE = 0x20; |
| 101 | 101 | ||
| 102 | #else | 102 | #else |
| 103 | 103 | ||
| @@ -147,22 +147,23 @@ const BitSet32 ABI_ALL_CALLEE_SAVED = BuildRegSet({ | |||
| 147 | Xbyak::util::r15, | 147 | Xbyak::util::r15, |
| 148 | }); | 148 | }); |
| 149 | 149 | ||
| 150 | constexpr size_t ABI_SHADOW_SPACE = 0; | 150 | constexpr std::size_t ABI_SHADOW_SPACE = 0; |
| 151 | 151 | ||
| 152 | #endif | 152 | #endif |
| 153 | 153 | ||
| 154 | inline void ABI_CalculateFrameSize(BitSet32 regs, size_t rsp_alignment, size_t needed_frame_size, | 154 | inline void ABI_CalculateFrameSize(BitSet32 regs, std::size_t rsp_alignment, |
| 155 | s32* out_subtraction, s32* out_xmm_offset) { | 155 | std::size_t needed_frame_size, s32* out_subtraction, |
| 156 | s32* out_xmm_offset) { | ||
| 156 | int count = (regs & ABI_ALL_GPRS).Count(); | 157 | int count = (regs & ABI_ALL_GPRS).Count(); |
| 157 | rsp_alignment -= count * 8; | 158 | rsp_alignment -= count * 8; |
| 158 | size_t subtraction = 0; | 159 | std::size_t subtraction = 0; |
| 159 | int xmm_count = (regs & ABI_ALL_XMMS).Count(); | 160 | int xmm_count = (regs & ABI_ALL_XMMS).Count(); |
| 160 | if (xmm_count) { | 161 | if (xmm_count) { |
| 161 | // If we have any XMMs to save, we must align the stack here. | 162 | // If we have any XMMs to save, we must align the stack here. |
| 162 | subtraction = rsp_alignment & 0xF; | 163 | subtraction = rsp_alignment & 0xF; |
| 163 | } | 164 | } |
| 164 | subtraction += 0x10 * xmm_count; | 165 | subtraction += 0x10 * xmm_count; |
| 165 | size_t xmm_base_subtraction = subtraction; | 166 | std::size_t xmm_base_subtraction = subtraction; |
| 166 | subtraction += needed_frame_size; | 167 | subtraction += needed_frame_size; |
| 167 | subtraction += ABI_SHADOW_SPACE; | 168 | subtraction += ABI_SHADOW_SPACE; |
| 168 | // Final alignment. | 169 | // Final alignment. |
| @@ -173,8 +174,9 @@ inline void ABI_CalculateFrameSize(BitSet32 regs, size_t rsp_alignment, size_t n | |||
| 173 | *out_xmm_offset = (s32)(subtraction - xmm_base_subtraction); | 174 | *out_xmm_offset = (s32)(subtraction - xmm_base_subtraction); |
| 174 | } | 175 | } |
| 175 | 176 | ||
| 176 | inline size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs, | 177 | inline std::size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs, |
| 177 | size_t rsp_alignment, size_t needed_frame_size = 0) { | 178 | std::size_t rsp_alignment, |
| 179 | std::size_t needed_frame_size = 0) { | ||
| 178 | s32 subtraction, xmm_offset; | 180 | s32 subtraction, xmm_offset; |
| 179 | ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset); | 181 | ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset); |
| 180 | 182 | ||
| @@ -195,7 +197,8 @@ inline size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet | |||
| 195 | } | 197 | } |
| 196 | 198 | ||
| 197 | inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs, | 199 | inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs, |
| 198 | size_t rsp_alignment, size_t needed_frame_size = 0) { | 200 | std::size_t rsp_alignment, |
| 201 | std::size_t needed_frame_size = 0) { | ||
| 199 | s32 subtraction, xmm_offset; | 202 | s32 subtraction, xmm_offset; |
| 200 | ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset); | 203 | ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size, &subtraction, &xmm_offset); |
| 201 | 204 | ||
diff --git a/src/common/x64/xbyak_util.h b/src/common/x64/xbyak_util.h index 02323a017..5cc8a8c76 100644 --- a/src/common/x64/xbyak_util.h +++ b/src/common/x64/xbyak_util.h | |||
| @@ -34,7 +34,7 @@ inline bool IsWithin2G(const Xbyak::CodeGenerator& code, uintptr_t target) { | |||
| 34 | template <typename T> | 34 | template <typename T> |
| 35 | inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) { | 35 | inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) { |
| 36 | static_assert(std::is_pointer_v<T>, "Argument must be a (function) pointer."); | 36 | static_assert(std::is_pointer_v<T>, "Argument must be a (function) pointer."); |
| 37 | size_t addr = reinterpret_cast<size_t>(f); | 37 | std::size_t addr = reinterpret_cast<std::size_t>(f); |
| 38 | if (IsWithin2G(code, addr)) { | 38 | if (IsWithin2G(code, addr)) { |
| 39 | code.call(f); | 39 | code.call(f); |
| 40 | } else { | 40 | } else { |
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 26f727d96..23fd6e920 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -32,6 +32,8 @@ add_library(core STATIC | |||
| 32 | file_sys/control_metadata.h | 32 | file_sys/control_metadata.h |
| 33 | file_sys/directory.h | 33 | file_sys/directory.h |
| 34 | file_sys/errors.h | 34 | file_sys/errors.h |
| 35 | file_sys/fsmitm_romfsbuild.cpp | ||
| 36 | file_sys/fsmitm_romfsbuild.h | ||
| 35 | file_sys/mode.h | 37 | file_sys/mode.h |
| 36 | file_sys/nca_metadata.cpp | 38 | file_sys/nca_metadata.cpp |
| 37 | file_sys/nca_metadata.h | 39 | file_sys/nca_metadata.h |
| @@ -59,10 +61,13 @@ add_library(core STATIC | |||
| 59 | file_sys/vfs.h | 61 | file_sys/vfs.h |
| 60 | file_sys/vfs_concat.cpp | 62 | file_sys/vfs_concat.cpp |
| 61 | file_sys/vfs_concat.h | 63 | file_sys/vfs_concat.h |
| 64 | file_sys/vfs_layered.cpp | ||
| 65 | file_sys/vfs_layered.h | ||
| 62 | file_sys/vfs_offset.cpp | 66 | file_sys/vfs_offset.cpp |
| 63 | file_sys/vfs_offset.h | 67 | file_sys/vfs_offset.h |
| 64 | file_sys/vfs_real.cpp | 68 | file_sys/vfs_real.cpp |
| 65 | file_sys/vfs_real.h | 69 | file_sys/vfs_real.h |
| 70 | file_sys/vfs_static.h | ||
| 66 | file_sys/vfs_vector.cpp | 71 | file_sys/vfs_vector.cpp |
| 67 | file_sys/vfs_vector.h | 72 | file_sys/vfs_vector.h |
| 68 | file_sys/xts_archive.cpp | 73 | file_sys/xts_archive.cpp |
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index c368745b1..59da33f30 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h | |||
| @@ -6,11 +6,14 @@ | |||
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | #include "core/hle/kernel/vm_manager.h" | 9 | |
| 10 | namespace Kernel { | ||
| 11 | enum class VMAPermission : u8; | ||
| 12 | } | ||
| 10 | 13 | ||
| 11 | namespace Core { | 14 | namespace Core { |
| 12 | 15 | ||
| 13 | /// Generic ARM11 CPU interface | 16 | /// Generic ARMv8 CPU interface |
| 14 | class ARM_Interface : NonCopyable { | 17 | class ARM_Interface : NonCopyable { |
| 15 | public: | 18 | public: |
| 16 | virtual ~ARM_Interface() {} | 19 | virtual ~ARM_Interface() {} |
| @@ -19,10 +22,16 @@ public: | |||
| 19 | std::array<u64, 31> cpu_registers; | 22 | std::array<u64, 31> cpu_registers; |
| 20 | u64 sp; | 23 | u64 sp; |
| 21 | u64 pc; | 24 | u64 pc; |
| 22 | u64 cpsr; | 25 | u32 pstate; |
| 23 | std::array<u128, 32> fpu_registers; | 26 | std::array<u8, 4> padding; |
| 24 | u64 fpscr; | 27 | std::array<u128, 32> vector_registers; |
| 28 | u32 fpcr; | ||
| 29 | u32 fpsr; | ||
| 30 | u64 tpidr; | ||
| 25 | }; | 31 | }; |
| 32 | // Internally within the kernel, it expects the AArch64 version of the | ||
| 33 | // thread context to be 800 bytes in size. | ||
| 34 | static_assert(sizeof(ThreadContext) == 0x320); | ||
| 26 | 35 | ||
| 27 | /// Runs the CPU until an event happens | 36 | /// Runs the CPU until an event happens |
| 28 | virtual void Run() = 0; | 37 | virtual void Run() = 0; |
| @@ -31,11 +40,11 @@ public: | |||
| 31 | virtual void Step() = 0; | 40 | virtual void Step() = 0; |
| 32 | 41 | ||
| 33 | /// Maps a backing memory region for the CPU | 42 | /// Maps a backing memory region for the CPU |
| 34 | virtual void MapBackingMemory(VAddr address, size_t size, u8* memory, | 43 | virtual void MapBackingMemory(VAddr address, std::size_t size, u8* memory, |
| 35 | Kernel::VMAPermission perms) = 0; | 44 | Kernel::VMAPermission perms) = 0; |
| 36 | 45 | ||
| 37 | /// Unmaps a region of memory that was previously mapped using MapBackingMemory | 46 | /// Unmaps a region of memory that was previously mapped using MapBackingMemory |
| 38 | virtual void UnmapMemory(VAddr address, size_t size) = 0; | 47 | virtual void UnmapMemory(VAddr address, std::size_t size) = 0; |
| 39 | 48 | ||
| 40 | /// Clear all instruction cache | 49 | /// Clear all instruction cache |
| 41 | virtual void ClearInstructionCache() = 0; | 50 | virtual void ClearInstructionCache() = 0; |
| @@ -69,42 +78,50 @@ public: | |||
| 69 | */ | 78 | */ |
| 70 | virtual void SetReg(int index, u64 value) = 0; | 79 | virtual void SetReg(int index, u64 value) = 0; |
| 71 | 80 | ||
| 72 | virtual u128 GetExtReg(int index) const = 0; | ||
| 73 | |||
| 74 | virtual void SetExtReg(int index, u128 value) = 0; | ||
| 75 | |||
| 76 | /** | 81 | /** |
| 77 | * Gets the value of a VFP register | 82 | * Gets the value of a specified vector register. |
| 78 | * @param index Register index (0-31) | 83 | * |
| 79 | * @return Returns the value in the register | 84 | * @param index The index of the vector register. |
| 85 | * @return the value within the vector register. | ||
| 80 | */ | 86 | */ |
| 81 | virtual u32 GetVFPReg(int index) const = 0; | 87 | virtual u128 GetVectorReg(int index) const = 0; |
| 82 | 88 | ||
| 83 | /** | 89 | /** |
| 84 | * Sets a VFP register to the given value | 90 | * Sets a given value into a vector register. |
| 85 | * @param index Register index (0-31) | 91 | * |
| 86 | * @param value Value to set register to | 92 | * @param index The index of the vector register. |
| 93 | * @param value The new value to place in the register. | ||
| 87 | */ | 94 | */ |
| 88 | virtual void SetVFPReg(int index, u32 value) = 0; | 95 | virtual void SetVectorReg(int index, u128 value) = 0; |
| 89 | 96 | ||
| 90 | /** | 97 | /** |
| 91 | * Get the current CPSR register | 98 | * Get the current PSTATE register |
| 92 | * @return Returns the value of the CPSR register | 99 | * @return Returns the value of the PSTATE register |
| 93 | */ | 100 | */ |
| 94 | virtual u32 GetCPSR() const = 0; | 101 | virtual u32 GetPSTATE() const = 0; |
| 95 | 102 | ||
| 96 | /** | 103 | /** |
| 97 | * Set the current CPSR register | 104 | * Set the current PSTATE register |
| 98 | * @param cpsr Value to set CPSR to | 105 | * @param pstate Value to set PSTATE to |
| 99 | */ | 106 | */ |
| 100 | virtual void SetCPSR(u32 cpsr) = 0; | 107 | virtual void SetPSTATE(u32 pstate) = 0; |
| 101 | 108 | ||
| 102 | virtual VAddr GetTlsAddress() const = 0; | 109 | virtual VAddr GetTlsAddress() const = 0; |
| 103 | 110 | ||
| 104 | virtual void SetTlsAddress(VAddr address) = 0; | 111 | virtual void SetTlsAddress(VAddr address) = 0; |
| 105 | 112 | ||
| 113 | /** | ||
| 114 | * Gets the value within the TPIDR_EL0 (read/write software thread ID) register. | ||
| 115 | * | ||
| 116 | * @return the value within the register. | ||
| 117 | */ | ||
| 106 | virtual u64 GetTPIDR_EL0() const = 0; | 118 | virtual u64 GetTPIDR_EL0() const = 0; |
| 107 | 119 | ||
| 120 | /** | ||
| 121 | * Sets a new value within the TPIDR_EL0 (read/write software thread ID) register. | ||
| 122 | * | ||
| 123 | * @param value The new value to place in the register. | ||
| 124 | */ | ||
| 108 | virtual void SetTPIDR_EL0(u64 value) = 0; | 125 | virtual void SetTPIDR_EL0(u64 value) = 0; |
| 109 | 126 | ||
| 110 | /** | 127 | /** |
| @@ -119,6 +136,7 @@ public: | |||
| 119 | */ | 136 | */ |
| 120 | virtual void LoadContext(const ThreadContext& ctx) = 0; | 137 | virtual void LoadContext(const ThreadContext& ctx) = 0; |
| 121 | 138 | ||
| 139 | /// Clears the exclusive monitor's state. | ||
| 122 | virtual void ClearExclusiveState() = 0; | 140 | virtual void ClearExclusiveState() = 0; |
| 123 | 141 | ||
| 124 | /// Prepare core for thread reschedule (if needed to correctly handle state) | 142 | /// Prepare core for thread reschedule (if needed to correctly handle state) |
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp index b47f04988..05cc84458 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp | |||
| @@ -12,8 +12,10 @@ | |||
| 12 | #include "core/core.h" | 12 | #include "core/core.h" |
| 13 | #include "core/core_cpu.h" | 13 | #include "core/core_cpu.h" |
| 14 | #include "core/core_timing.h" | 14 | #include "core/core_timing.h" |
| 15 | #include "core/gdbstub/gdbstub.h" | ||
| 15 | #include "core/hle/kernel/process.h" | 16 | #include "core/hle/kernel/process.h" |
| 16 | #include "core/hle/kernel/svc.h" | 17 | #include "core/hle/kernel/svc.h" |
| 18 | #include "core/hle/kernel/vm_manager.h" | ||
| 17 | #include "core/memory.h" | 19 | #include "core/memory.h" |
| 18 | 20 | ||
| 19 | namespace Core { | 21 | namespace Core { |
| @@ -58,7 +60,7 @@ public: | |||
| 58 | Memory::Write64(vaddr + 8, value[1]); | 60 | Memory::Write64(vaddr + 8, value[1]); |
| 59 | } | 61 | } |
| 60 | 62 | ||
| 61 | void InterpreterFallback(u64 pc, size_t num_instructions) override { | 63 | void InterpreterFallback(u64 pc, std::size_t num_instructions) override { |
| 62 | LOG_INFO(Core_ARM, "Unicorn fallback @ 0x{:X} for {} instructions (instr = {:08X})", pc, | 64 | LOG_INFO(Core_ARM, "Unicorn fallback @ 0x{:X} for {} instructions (instr = {:08X})", pc, |
| 63 | num_instructions, MemoryReadCode(pc)); | 65 | num_instructions, MemoryReadCode(pc)); |
| 64 | 66 | ||
| @@ -79,9 +81,20 @@ public: | |||
| 79 | case Dynarmic::A64::Exception::SendEventLocal: | 81 | case Dynarmic::A64::Exception::SendEventLocal: |
| 80 | case Dynarmic::A64::Exception::Yield: | 82 | case Dynarmic::A64::Exception::Yield: |
| 81 | return; | 83 | return; |
| 84 | case Dynarmic::A64::Exception::Breakpoint: | ||
| 85 | if (GDBStub::IsServerEnabled()) { | ||
| 86 | parent.jit->HaltExecution(); | ||
| 87 | parent.SetPC(pc); | ||
| 88 | Kernel::Thread* thread = Kernel::GetCurrentThread(); | ||
| 89 | parent.SaveContext(thread->context); | ||
| 90 | GDBStub::Break(); | ||
| 91 | GDBStub::SendTrap(thread, 5); | ||
| 92 | return; | ||
| 93 | } | ||
| 94 | [[fallthrough]]; | ||
| 82 | default: | 95 | default: |
| 83 | ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:X})", | 96 | ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:X})", |
| 84 | static_cast<size_t>(exception), pc); | 97 | static_cast<std::size_t>(exception), pc); |
| 85 | } | 98 | } |
| 86 | } | 99 | } |
| 87 | 100 | ||
| @@ -110,13 +123,14 @@ public: | |||
| 110 | } | 123 | } |
| 111 | 124 | ||
| 112 | ARM_Dynarmic& parent; | 125 | ARM_Dynarmic& parent; |
| 113 | size_t num_interpreted_instructions = 0; | 126 | std::size_t num_interpreted_instructions = 0; |
| 114 | u64 tpidrro_el0 = 0; | 127 | u64 tpidrro_el0 = 0; |
| 115 | u64 tpidr_el0 = 0; | 128 | u64 tpidr_el0 = 0; |
| 116 | }; | 129 | }; |
| 117 | 130 | ||
| 118 | std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const { | 131 | std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const { |
| 119 | auto** const page_table = Core::CurrentProcess()->vm_manager.page_table.pointers.data(); | 132 | auto& current_process = Core::CurrentProcess(); |
| 133 | auto** const page_table = current_process->VMManager().page_table.pointers.data(); | ||
| 120 | 134 | ||
| 121 | Dynarmic::A64::UserConfig config; | 135 | Dynarmic::A64::UserConfig config; |
| 122 | 136 | ||
| @@ -125,7 +139,7 @@ std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const { | |||
| 125 | 139 | ||
| 126 | // Memory | 140 | // Memory |
| 127 | config.page_table = reinterpret_cast<void**>(page_table); | 141 | config.page_table = reinterpret_cast<void**>(page_table); |
| 128 | config.page_table_address_space_bits = Memory::ADDRESS_SPACE_BITS; | 142 | config.page_table_address_space_bits = current_process->VMManager().GetAddressSpaceWidth(); |
| 129 | config.silently_mirror_page_table = false; | 143 | config.silently_mirror_page_table = false; |
| 130 | 144 | ||
| 131 | // Multi-process state | 145 | // Multi-process state |
| @@ -157,10 +171,11 @@ void ARM_Dynarmic::Step() { | |||
| 157 | cb->InterpreterFallback(jit->GetPC(), 1); | 171 | cb->InterpreterFallback(jit->GetPC(), 1); |
| 158 | } | 172 | } |
| 159 | 173 | ||
| 160 | ARM_Dynarmic::ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, size_t core_index) | 174 | ARM_Dynarmic::ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, |
| 175 | std::size_t core_index) | ||
| 161 | : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), core_index{core_index}, | 176 | : cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), core_index{core_index}, |
| 162 | exclusive_monitor{std::dynamic_pointer_cast<DynarmicExclusiveMonitor>(exclusive_monitor)} { | 177 | exclusive_monitor{std::dynamic_pointer_cast<DynarmicExclusiveMonitor>(exclusive_monitor)} { |
| 163 | ThreadContext ctx; | 178 | ThreadContext ctx{}; |
| 164 | inner_unicorn.SaveContext(ctx); | 179 | inner_unicorn.SaveContext(ctx); |
| 165 | PageTableChanged(); | 180 | PageTableChanged(); |
| 166 | LoadContext(ctx); | 181 | LoadContext(ctx); |
| @@ -168,12 +183,12 @@ ARM_Dynarmic::ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, | |||
| 168 | 183 | ||
| 169 | ARM_Dynarmic::~ARM_Dynarmic() = default; | 184 | ARM_Dynarmic::~ARM_Dynarmic() = default; |
| 170 | 185 | ||
| 171 | void ARM_Dynarmic::MapBackingMemory(u64 address, size_t size, u8* memory, | 186 | void ARM_Dynarmic::MapBackingMemory(u64 address, std::size_t size, u8* memory, |
| 172 | Kernel::VMAPermission perms) { | 187 | Kernel::VMAPermission perms) { |
| 173 | inner_unicorn.MapBackingMemory(address, size, memory, perms); | 188 | inner_unicorn.MapBackingMemory(address, size, memory, perms); |
| 174 | } | 189 | } |
| 175 | 190 | ||
| 176 | void ARM_Dynarmic::UnmapMemory(u64 address, size_t size) { | 191 | void ARM_Dynarmic::UnmapMemory(u64 address, std::size_t size) { |
| 177 | inner_unicorn.UnmapMemory(address, size); | 192 | inner_unicorn.UnmapMemory(address, size); |
| 178 | } | 193 | } |
| 179 | 194 | ||
| @@ -193,29 +208,20 @@ void ARM_Dynarmic::SetReg(int index, u64 value) { | |||
| 193 | jit->SetRegister(index, value); | 208 | jit->SetRegister(index, value); |
| 194 | } | 209 | } |
| 195 | 210 | ||
| 196 | u128 ARM_Dynarmic::GetExtReg(int index) const { | 211 | u128 ARM_Dynarmic::GetVectorReg(int index) const { |
| 197 | return jit->GetVector(index); | 212 | return jit->GetVector(index); |
| 198 | } | 213 | } |
| 199 | 214 | ||
| 200 | void ARM_Dynarmic::SetExtReg(int index, u128 value) { | 215 | void ARM_Dynarmic::SetVectorReg(int index, u128 value) { |
| 201 | jit->SetVector(index, value); | 216 | jit->SetVector(index, value); |
| 202 | } | 217 | } |
| 203 | 218 | ||
| 204 | u32 ARM_Dynarmic::GetVFPReg(int /*index*/) const { | 219 | u32 ARM_Dynarmic::GetPSTATE() const { |
| 205 | UNIMPLEMENTED(); | ||
| 206 | return {}; | ||
| 207 | } | ||
| 208 | |||
| 209 | void ARM_Dynarmic::SetVFPReg(int /*index*/, u32 /*value*/) { | ||
| 210 | UNIMPLEMENTED(); | ||
| 211 | } | ||
| 212 | |||
| 213 | u32 ARM_Dynarmic::GetCPSR() const { | ||
| 214 | return jit->GetPstate(); | 220 | return jit->GetPstate(); |
| 215 | } | 221 | } |
| 216 | 222 | ||
| 217 | void ARM_Dynarmic::SetCPSR(u32 cpsr) { | 223 | void ARM_Dynarmic::SetPSTATE(u32 pstate) { |
| 218 | jit->SetPstate(cpsr); | 224 | jit->SetPstate(pstate); |
| 219 | } | 225 | } |
| 220 | 226 | ||
| 221 | u64 ARM_Dynarmic::GetTlsAddress() const { | 227 | u64 ARM_Dynarmic::GetTlsAddress() const { |
| @@ -238,18 +244,22 @@ void ARM_Dynarmic::SaveContext(ThreadContext& ctx) { | |||
| 238 | ctx.cpu_registers = jit->GetRegisters(); | 244 | ctx.cpu_registers = jit->GetRegisters(); |
| 239 | ctx.sp = jit->GetSP(); | 245 | ctx.sp = jit->GetSP(); |
| 240 | ctx.pc = jit->GetPC(); | 246 | ctx.pc = jit->GetPC(); |
| 241 | ctx.cpsr = jit->GetPstate(); | 247 | ctx.pstate = jit->GetPstate(); |
| 242 | ctx.fpu_registers = jit->GetVectors(); | 248 | ctx.vector_registers = jit->GetVectors(); |
| 243 | ctx.fpscr = jit->GetFpcr(); | 249 | ctx.fpcr = jit->GetFpcr(); |
| 250 | ctx.fpsr = jit->GetFpsr(); | ||
| 251 | ctx.tpidr = cb->tpidr_el0; | ||
| 244 | } | 252 | } |
| 245 | 253 | ||
| 246 | void ARM_Dynarmic::LoadContext(const ThreadContext& ctx) { | 254 | void ARM_Dynarmic::LoadContext(const ThreadContext& ctx) { |
| 247 | jit->SetRegisters(ctx.cpu_registers); | 255 | jit->SetRegisters(ctx.cpu_registers); |
| 248 | jit->SetSP(ctx.sp); | 256 | jit->SetSP(ctx.sp); |
| 249 | jit->SetPC(ctx.pc); | 257 | jit->SetPC(ctx.pc); |
| 250 | jit->SetPstate(static_cast<u32>(ctx.cpsr)); | 258 | jit->SetPstate(ctx.pstate); |
| 251 | jit->SetVectors(ctx.fpu_registers); | 259 | jit->SetVectors(ctx.vector_registers); |
| 252 | jit->SetFpcr(static_cast<u32>(ctx.fpscr)); | 260 | jit->SetFpcr(ctx.fpcr); |
| 261 | jit->SetFpsr(ctx.fpsr); | ||
| 262 | SetTPIDR_EL0(ctx.tpidr); | ||
| 253 | } | 263 | } |
| 254 | 264 | ||
| 255 | void ARM_Dynarmic::PrepareReschedule() { | 265 | void ARM_Dynarmic::PrepareReschedule() { |
| @@ -269,10 +279,10 @@ void ARM_Dynarmic::PageTableChanged() { | |||
| 269 | current_page_table = Memory::GetCurrentPageTable(); | 279 | current_page_table = Memory::GetCurrentPageTable(); |
| 270 | } | 280 | } |
| 271 | 281 | ||
| 272 | DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(size_t core_count) : monitor(core_count) {} | 282 | DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(std::size_t core_count) : monitor(core_count) {} |
| 273 | DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default; | 283 | DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default; |
| 274 | 284 | ||
| 275 | void DynarmicExclusiveMonitor::SetExclusive(size_t core_index, VAddr addr) { | 285 | void DynarmicExclusiveMonitor::SetExclusive(std::size_t core_index, VAddr addr) { |
| 276 | // Size doesn't actually matter. | 286 | // Size doesn't actually matter. |
| 277 | monitor.Mark(core_index, addr, 16); | 287 | monitor.Mark(core_index, addr, 16); |
| 278 | } | 288 | } |
| @@ -281,30 +291,30 @@ void DynarmicExclusiveMonitor::ClearExclusive() { | |||
| 281 | monitor.Clear(); | 291 | monitor.Clear(); |
| 282 | } | 292 | } |
| 283 | 293 | ||
| 284 | bool DynarmicExclusiveMonitor::ExclusiveWrite8(size_t core_index, VAddr vaddr, u8 value) { | 294 | bool DynarmicExclusiveMonitor::ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) { |
| 285 | return monitor.DoExclusiveOperation(core_index, vaddr, 1, | 295 | return monitor.DoExclusiveOperation(core_index, vaddr, 1, |
| 286 | [&] { Memory::Write8(vaddr, value); }); | 296 | [&] { Memory::Write8(vaddr, value); }); |
| 287 | } | 297 | } |
| 288 | 298 | ||
| 289 | bool DynarmicExclusiveMonitor::ExclusiveWrite16(size_t core_index, VAddr vaddr, u16 value) { | 299 | bool DynarmicExclusiveMonitor::ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) { |
| 290 | return monitor.DoExclusiveOperation(core_index, vaddr, 2, | 300 | return monitor.DoExclusiveOperation(core_index, vaddr, 2, |
| 291 | [&] { Memory::Write16(vaddr, value); }); | 301 | [&] { Memory::Write16(vaddr, value); }); |
| 292 | } | 302 | } |
| 293 | 303 | ||
| 294 | bool DynarmicExclusiveMonitor::ExclusiveWrite32(size_t core_index, VAddr vaddr, u32 value) { | 304 | bool DynarmicExclusiveMonitor::ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) { |
| 295 | return monitor.DoExclusiveOperation(core_index, vaddr, 4, | 305 | return monitor.DoExclusiveOperation(core_index, vaddr, 4, |
| 296 | [&] { Memory::Write32(vaddr, value); }); | 306 | [&] { Memory::Write32(vaddr, value); }); |
| 297 | } | 307 | } |
| 298 | 308 | ||
| 299 | bool DynarmicExclusiveMonitor::ExclusiveWrite64(size_t core_index, VAddr vaddr, u64 value) { | 309 | bool DynarmicExclusiveMonitor::ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) { |
| 300 | return monitor.DoExclusiveOperation(core_index, vaddr, 8, | 310 | return monitor.DoExclusiveOperation(core_index, vaddr, 8, |
| 301 | [&] { Memory::Write64(vaddr, value); }); | 311 | [&] { Memory::Write64(vaddr, value); }); |
| 302 | } | 312 | } |
| 303 | 313 | ||
| 304 | bool DynarmicExclusiveMonitor::ExclusiveWrite128(size_t core_index, VAddr vaddr, u128 value) { | 314 | bool DynarmicExclusiveMonitor::ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) { |
| 305 | return monitor.DoExclusiveOperation(core_index, vaddr, 16, [&] { | 315 | return monitor.DoExclusiveOperation(core_index, vaddr, 16, [&] { |
| 306 | Memory::Write64(vaddr, value[0]); | 316 | Memory::Write64(vaddr + 0, value[0]); |
| 307 | Memory::Write64(vaddr, value[1]); | 317 | Memory::Write64(vaddr + 8, value[1]); |
| 308 | }); | 318 | }); |
| 309 | } | 319 | } |
| 310 | 320 | ||
diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h index 3bdfd8cd9..4ee92ee27 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.h +++ b/src/core/arm/dynarmic/arm_dynarmic.h | |||
| @@ -12,6 +12,10 @@ | |||
| 12 | #include "core/arm/exclusive_monitor.h" | 12 | #include "core/arm/exclusive_monitor.h" |
| 13 | #include "core/arm/unicorn/arm_unicorn.h" | 13 | #include "core/arm/unicorn/arm_unicorn.h" |
| 14 | 14 | ||
| 15 | namespace Memory { | ||
| 16 | struct PageTable; | ||
| 17 | } | ||
| 18 | |||
| 15 | namespace Core { | 19 | namespace Core { |
| 16 | 20 | ||
| 17 | class ARM_Dynarmic_Callbacks; | 21 | class ARM_Dynarmic_Callbacks; |
| @@ -19,24 +23,22 @@ class DynarmicExclusiveMonitor; | |||
| 19 | 23 | ||
| 20 | class ARM_Dynarmic final : public ARM_Interface { | 24 | class ARM_Dynarmic final : public ARM_Interface { |
| 21 | public: | 25 | public: |
| 22 | ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, size_t core_index); | 26 | ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, std::size_t core_index); |
| 23 | ~ARM_Dynarmic(); | 27 | ~ARM_Dynarmic(); |
| 24 | 28 | ||
| 25 | void MapBackingMemory(VAddr address, size_t size, u8* memory, | 29 | void MapBackingMemory(VAddr address, std::size_t size, u8* memory, |
| 26 | Kernel::VMAPermission perms) override; | 30 | Kernel::VMAPermission perms) override; |
| 27 | void UnmapMemory(u64 address, size_t size) override; | 31 | void UnmapMemory(u64 address, std::size_t size) override; |
| 28 | void SetPC(u64 pc) override; | 32 | void SetPC(u64 pc) override; |
| 29 | u64 GetPC() const override; | 33 | u64 GetPC() const override; |
| 30 | u64 GetReg(int index) const override; | 34 | u64 GetReg(int index) const override; |
| 31 | void SetReg(int index, u64 value) override; | 35 | void SetReg(int index, u64 value) override; |
| 32 | u128 GetExtReg(int index) const override; | 36 | u128 GetVectorReg(int index) const override; |
| 33 | void SetExtReg(int index, u128 value) override; | 37 | void SetVectorReg(int index, u128 value) override; |
| 34 | u32 GetVFPReg(int index) const override; | 38 | u32 GetPSTATE() const override; |
| 35 | void SetVFPReg(int index, u32 value) override; | 39 | void SetPSTATE(u32 pstate) override; |
| 36 | u32 GetCPSR() const override; | ||
| 37 | void Run() override; | 40 | void Run() override; |
| 38 | void Step() override; | 41 | void Step() override; |
| 39 | void SetCPSR(u32 cpsr) override; | ||
| 40 | VAddr GetTlsAddress() const override; | 42 | VAddr GetTlsAddress() const override; |
| 41 | void SetTlsAddress(VAddr address) override; | 43 | void SetTlsAddress(VAddr address) override; |
| 42 | void SetTPIDR_EL0(u64 value) override; | 44 | void SetTPIDR_EL0(u64 value) override; |
| @@ -59,7 +61,7 @@ private: | |||
| 59 | std::unique_ptr<Dynarmic::A64::Jit> jit; | 61 | std::unique_ptr<Dynarmic::A64::Jit> jit; |
| 60 | ARM_Unicorn inner_unicorn; | 62 | ARM_Unicorn inner_unicorn; |
| 61 | 63 | ||
| 62 | size_t core_index; | 64 | std::size_t core_index; |
| 63 | std::shared_ptr<DynarmicExclusiveMonitor> exclusive_monitor; | 65 | std::shared_ptr<DynarmicExclusiveMonitor> exclusive_monitor; |
| 64 | 66 | ||
| 65 | Memory::PageTable* current_page_table = nullptr; | 67 | Memory::PageTable* current_page_table = nullptr; |
| @@ -67,17 +69,17 @@ private: | |||
| 67 | 69 | ||
| 68 | class DynarmicExclusiveMonitor final : public ExclusiveMonitor { | 70 | class DynarmicExclusiveMonitor final : public ExclusiveMonitor { |
| 69 | public: | 71 | public: |
| 70 | explicit DynarmicExclusiveMonitor(size_t core_count); | 72 | explicit DynarmicExclusiveMonitor(std::size_t core_count); |
| 71 | ~DynarmicExclusiveMonitor(); | 73 | ~DynarmicExclusiveMonitor(); |
| 72 | 74 | ||
| 73 | void SetExclusive(size_t core_index, VAddr addr) override; | 75 | void SetExclusive(std::size_t core_index, VAddr addr) override; |
| 74 | void ClearExclusive() override; | 76 | void ClearExclusive() override; |
| 75 | 77 | ||
| 76 | bool ExclusiveWrite8(size_t core_index, VAddr vaddr, u8 value) override; | 78 | bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) override; |
| 77 | bool ExclusiveWrite16(size_t core_index, VAddr vaddr, u16 value) override; | 79 | bool ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) override; |
| 78 | bool ExclusiveWrite32(size_t core_index, VAddr vaddr, u32 value) override; | 80 | bool ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) override; |
| 79 | bool ExclusiveWrite64(size_t core_index, VAddr vaddr, u64 value) override; | 81 | bool ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) override; |
| 80 | bool ExclusiveWrite128(size_t core_index, VAddr vaddr, u128 value) override; | 82 | bool ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) override; |
| 81 | 83 | ||
| 82 | private: | 84 | private: |
| 83 | friend class ARM_Dynarmic; | 85 | friend class ARM_Dynarmic; |
diff --git a/src/core/arm/exclusive_monitor.h b/src/core/arm/exclusive_monitor.h index 6f9b51573..f59aca667 100644 --- a/src/core/arm/exclusive_monitor.h +++ b/src/core/arm/exclusive_monitor.h | |||
| @@ -12,14 +12,14 @@ class ExclusiveMonitor { | |||
| 12 | public: | 12 | public: |
| 13 | virtual ~ExclusiveMonitor(); | 13 | virtual ~ExclusiveMonitor(); |
| 14 | 14 | ||
| 15 | virtual void SetExclusive(size_t core_index, VAddr addr) = 0; | 15 | virtual void SetExclusive(std::size_t core_index, VAddr addr) = 0; |
| 16 | virtual void ClearExclusive() = 0; | 16 | virtual void ClearExclusive() = 0; |
| 17 | 17 | ||
| 18 | virtual bool ExclusiveWrite8(size_t core_index, VAddr vaddr, u8 value) = 0; | 18 | virtual bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) = 0; |
| 19 | virtual bool ExclusiveWrite16(size_t core_index, VAddr vaddr, u16 value) = 0; | 19 | virtual bool ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) = 0; |
| 20 | virtual bool ExclusiveWrite32(size_t core_index, VAddr vaddr, u32 value) = 0; | 20 | virtual bool ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) = 0; |
| 21 | virtual bool ExclusiveWrite64(size_t core_index, VAddr vaddr, u64 value) = 0; | 21 | virtual bool ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) = 0; |
| 22 | virtual bool ExclusiveWrite128(size_t core_index, VAddr vaddr, u128 value) = 0; | 22 | virtual bool ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) = 0; |
| 23 | }; | 23 | }; |
| 24 | 24 | ||
| 25 | } // namespace Core | 25 | } // namespace Core |
diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp index 4c4de2623..e218a0b15 100644 --- a/src/core/arm/unicorn/arm_unicorn.cpp +++ b/src/core/arm/unicorn/arm_unicorn.cpp | |||
| @@ -90,12 +90,12 @@ ARM_Unicorn::~ARM_Unicorn() { | |||
| 90 | CHECKED(uc_close(uc)); | 90 | CHECKED(uc_close(uc)); |
| 91 | } | 91 | } |
| 92 | 92 | ||
| 93 | void ARM_Unicorn::MapBackingMemory(VAddr address, size_t size, u8* memory, | 93 | void ARM_Unicorn::MapBackingMemory(VAddr address, std::size_t size, u8* memory, |
| 94 | Kernel::VMAPermission perms) { | 94 | Kernel::VMAPermission perms) { |
| 95 | CHECKED(uc_mem_map_ptr(uc, address, size, static_cast<u32>(perms), memory)); | 95 | CHECKED(uc_mem_map_ptr(uc, address, size, static_cast<u32>(perms), memory)); |
| 96 | } | 96 | } |
| 97 | 97 | ||
| 98 | void ARM_Unicorn::UnmapMemory(VAddr address, size_t size) { | 98 | void ARM_Unicorn::UnmapMemory(VAddr address, std::size_t size) { |
| 99 | CHECKED(uc_mem_unmap(uc, address, size)); | 99 | CHECKED(uc_mem_unmap(uc, address, size)); |
| 100 | } | 100 | } |
| 101 | 101 | ||
| @@ -131,33 +131,24 @@ void ARM_Unicorn::SetReg(int regn, u64 val) { | |||
| 131 | CHECKED(uc_reg_write(uc, treg, &val)); | 131 | CHECKED(uc_reg_write(uc, treg, &val)); |
| 132 | } | 132 | } |
| 133 | 133 | ||
| 134 | u128 ARM_Unicorn::GetExtReg(int /*index*/) const { | 134 | u128 ARM_Unicorn::GetVectorReg(int /*index*/) const { |
| 135 | UNIMPLEMENTED(); | 135 | UNIMPLEMENTED(); |
| 136 | static constexpr u128 res{}; | 136 | static constexpr u128 res{}; |
| 137 | return res; | 137 | return res; |
| 138 | } | 138 | } |
| 139 | 139 | ||
| 140 | void ARM_Unicorn::SetExtReg(int /*index*/, u128 /*value*/) { | 140 | void ARM_Unicorn::SetVectorReg(int /*index*/, u128 /*value*/) { |
| 141 | UNIMPLEMENTED(); | 141 | UNIMPLEMENTED(); |
| 142 | } | 142 | } |
| 143 | 143 | ||
| 144 | u32 ARM_Unicorn::GetVFPReg(int /*index*/) const { | 144 | u32 ARM_Unicorn::GetPSTATE() const { |
| 145 | UNIMPLEMENTED(); | ||
| 146 | return {}; | ||
| 147 | } | ||
| 148 | |||
| 149 | void ARM_Unicorn::SetVFPReg(int /*index*/, u32 /*value*/) { | ||
| 150 | UNIMPLEMENTED(); | ||
| 151 | } | ||
| 152 | |||
| 153 | u32 ARM_Unicorn::GetCPSR() const { | ||
| 154 | u64 nzcv{}; | 145 | u64 nzcv{}; |
| 155 | CHECKED(uc_reg_read(uc, UC_ARM64_REG_NZCV, &nzcv)); | 146 | CHECKED(uc_reg_read(uc, UC_ARM64_REG_NZCV, &nzcv)); |
| 156 | return static_cast<u32>(nzcv); | 147 | return static_cast<u32>(nzcv); |
| 157 | } | 148 | } |
| 158 | 149 | ||
| 159 | void ARM_Unicorn::SetCPSR(u32 cpsr) { | 150 | void ARM_Unicorn::SetPSTATE(u32 pstate) { |
| 160 | u64 nzcv = cpsr; | 151 | u64 nzcv = pstate; |
| 161 | CHECKED(uc_reg_write(uc, UC_ARM64_REG_NZCV, &nzcv)); | 152 | CHECKED(uc_reg_write(uc, UC_ARM64_REG_NZCV, &nzcv)); |
| 162 | } | 153 | } |
| 163 | 154 | ||
| @@ -219,7 +210,7 @@ void ARM_Unicorn::SaveContext(ThreadContext& ctx) { | |||
| 219 | 210 | ||
| 220 | CHECKED(uc_reg_read(uc, UC_ARM64_REG_SP, &ctx.sp)); | 211 | CHECKED(uc_reg_read(uc, UC_ARM64_REG_SP, &ctx.sp)); |
| 221 | CHECKED(uc_reg_read(uc, UC_ARM64_REG_PC, &ctx.pc)); | 212 | CHECKED(uc_reg_read(uc, UC_ARM64_REG_PC, &ctx.pc)); |
| 222 | CHECKED(uc_reg_read(uc, UC_ARM64_REG_NZCV, &ctx.cpsr)); | 213 | CHECKED(uc_reg_read(uc, UC_ARM64_REG_NZCV, &ctx.pstate)); |
| 223 | 214 | ||
| 224 | for (auto i = 0; i < 29; ++i) { | 215 | for (auto i = 0; i < 29; ++i) { |
| 225 | uregs[i] = UC_ARM64_REG_X0 + i; | 216 | uregs[i] = UC_ARM64_REG_X0 + i; |
| @@ -234,7 +225,7 @@ void ARM_Unicorn::SaveContext(ThreadContext& ctx) { | |||
| 234 | 225 | ||
| 235 | for (int i = 0; i < 32; ++i) { | 226 | for (int i = 0; i < 32; ++i) { |
| 236 | uregs[i] = UC_ARM64_REG_Q0 + i; | 227 | uregs[i] = UC_ARM64_REG_Q0 + i; |
| 237 | tregs[i] = &ctx.fpu_registers[i]; | 228 | tregs[i] = &ctx.vector_registers[i]; |
| 238 | } | 229 | } |
| 239 | 230 | ||
| 240 | CHECKED(uc_reg_read_batch(uc, uregs, tregs, 32)); | 231 | CHECKED(uc_reg_read_batch(uc, uregs, tregs, 32)); |
| @@ -246,7 +237,7 @@ void ARM_Unicorn::LoadContext(const ThreadContext& ctx) { | |||
| 246 | 237 | ||
| 247 | CHECKED(uc_reg_write(uc, UC_ARM64_REG_SP, &ctx.sp)); | 238 | CHECKED(uc_reg_write(uc, UC_ARM64_REG_SP, &ctx.sp)); |
| 248 | CHECKED(uc_reg_write(uc, UC_ARM64_REG_PC, &ctx.pc)); | 239 | CHECKED(uc_reg_write(uc, UC_ARM64_REG_PC, &ctx.pc)); |
| 249 | CHECKED(uc_reg_write(uc, UC_ARM64_REG_NZCV, &ctx.cpsr)); | 240 | CHECKED(uc_reg_write(uc, UC_ARM64_REG_NZCV, &ctx.pstate)); |
| 250 | 241 | ||
| 251 | for (int i = 0; i < 29; ++i) { | 242 | for (int i = 0; i < 29; ++i) { |
| 252 | uregs[i] = UC_ARM64_REG_X0 + i; | 243 | uregs[i] = UC_ARM64_REG_X0 + i; |
| @@ -261,7 +252,7 @@ void ARM_Unicorn::LoadContext(const ThreadContext& ctx) { | |||
| 261 | 252 | ||
| 262 | for (auto i = 0; i < 32; ++i) { | 253 | for (auto i = 0; i < 32; ++i) { |
| 263 | uregs[i] = UC_ARM64_REG_Q0 + i; | 254 | uregs[i] = UC_ARM64_REG_Q0 + i; |
| 264 | tregs[i] = (void*)&ctx.fpu_registers[i]; | 255 | tregs[i] = (void*)&ctx.vector_registers[i]; |
| 265 | } | 256 | } |
| 266 | 257 | ||
| 267 | CHECKED(uc_reg_write_batch(uc, uregs, tregs, 32)); | 258 | CHECKED(uc_reg_write_batch(uc, uregs, tregs, 32)); |
diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h index bd6b2f723..75761950b 100644 --- a/src/core/arm/unicorn/arm_unicorn.h +++ b/src/core/arm/unicorn/arm_unicorn.h | |||
| @@ -15,19 +15,17 @@ class ARM_Unicorn final : public ARM_Interface { | |||
| 15 | public: | 15 | public: |
| 16 | ARM_Unicorn(); | 16 | ARM_Unicorn(); |
| 17 | ~ARM_Unicorn(); | 17 | ~ARM_Unicorn(); |
| 18 | void MapBackingMemory(VAddr address, size_t size, u8* memory, | 18 | void MapBackingMemory(VAddr address, std::size_t size, u8* memory, |
| 19 | Kernel::VMAPermission perms) override; | 19 | Kernel::VMAPermission perms) override; |
| 20 | void UnmapMemory(VAddr address, size_t size) override; | 20 | void UnmapMemory(VAddr address, std::size_t size) override; |
| 21 | void SetPC(u64 pc) override; | 21 | void SetPC(u64 pc) override; |
| 22 | u64 GetPC() const override; | 22 | u64 GetPC() const override; |
| 23 | u64 GetReg(int index) const override; | 23 | u64 GetReg(int index) const override; |
| 24 | void SetReg(int index, u64 value) override; | 24 | void SetReg(int index, u64 value) override; |
| 25 | u128 GetExtReg(int index) const override; | 25 | u128 GetVectorReg(int index) const override; |
| 26 | void SetExtReg(int index, u128 value) override; | 26 | void SetVectorReg(int index, u128 value) override; |
| 27 | u32 GetVFPReg(int index) const override; | 27 | u32 GetPSTATE() const override; |
| 28 | void SetVFPReg(int index, u32 value) override; | 28 | void SetPSTATE(u32 pstate) override; |
| 29 | u32 GetCPSR() const override; | ||
| 30 | void SetCPSR(u32 cpsr) override; | ||
| 31 | VAddr GetTlsAddress() const override; | 29 | VAddr GetTlsAddress() const override; |
| 32 | void SetTlsAddress(VAddr address) override; | 30 | void SetTlsAddress(VAddr address) override; |
| 33 | void SetTPIDR_EL0(u64 value) override; | 31 | void SetTPIDR_EL0(u64 value) override; |
diff --git a/src/core/core.cpp b/src/core/core.cpp index 713ee17c1..b6acfb3e4 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -64,7 +64,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, | |||
| 64 | if (concat.empty()) | 64 | if (concat.empty()) |
| 65 | return nullptr; | 65 | return nullptr; |
| 66 | 66 | ||
| 67 | return FileSys::ConcatenateFiles(concat, dir->GetName()); | 67 | return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(concat, dir->GetName()); |
| 68 | } | 68 | } |
| 69 | 69 | ||
| 70 | return vfs->OpenFile(path, FileSys::Mode::Read); | 70 | return vfs->OpenFile(path, FileSys::Mode::Read); |
| @@ -140,7 +140,7 @@ struct System::Impl { | |||
| 140 | 140 | ||
| 141 | cpu_barrier = std::make_shared<CpuBarrier>(); | 141 | cpu_barrier = std::make_shared<CpuBarrier>(); |
| 142 | cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size()); | 142 | cpu_exclusive_monitor = Cpu::MakeExclusiveMonitor(cpu_cores.size()); |
| 143 | for (size_t index = 0; index < cpu_cores.size(); ++index) { | 143 | for (std::size_t index = 0; index < cpu_cores.size(); ++index) { |
| 144 | cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index); | 144 | cpu_cores[index] = std::make_shared<Cpu>(cpu_exclusive_monitor, cpu_barrier, index); |
| 145 | } | 145 | } |
| 146 | 146 | ||
| @@ -161,7 +161,7 @@ struct System::Impl { | |||
| 161 | // CPU core 0 is run on the main thread | 161 | // CPU core 0 is run on the main thread |
| 162 | thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0]; | 162 | thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0]; |
| 163 | if (Settings::values.use_multi_core) { | 163 | if (Settings::values.use_multi_core) { |
| 164 | for (size_t index = 0; index < cpu_core_threads.size(); ++index) { | 164 | for (std::size_t index = 0; index < cpu_core_threads.size(); ++index) { |
| 165 | cpu_core_threads[index] = | 165 | cpu_core_threads[index] = |
| 166 | std::make_unique<std::thread>(RunCpuCore, cpu_cores[index + 1]); | 166 | std::make_unique<std::thread>(RunCpuCore, cpu_cores[index + 1]); |
| 167 | thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1]; | 167 | thread_to_cpu[cpu_core_threads[index]->get_id()] = cpu_cores[index + 1]; |
| @@ -202,7 +202,7 @@ struct System::Impl { | |||
| 202 | return init_result; | 202 | return init_result; |
| 203 | } | 203 | } |
| 204 | 204 | ||
| 205 | const Loader::ResultStatus load_result{app_loader->Load(kernel.CurrentProcess())}; | 205 | const Loader::ResultStatus load_result{app_loader->Load(*kernel.CurrentProcess())}; |
| 206 | if (load_result != Loader::ResultStatus::Success) { | 206 | if (load_result != Loader::ResultStatus::Success) { |
| 207 | LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result)); | 207 | LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result)); |
| 208 | Shutdown(); | 208 | Shutdown(); |
| @@ -285,7 +285,7 @@ struct System::Impl { | |||
| 285 | std::shared_ptr<CpuBarrier> cpu_barrier; | 285 | std::shared_ptr<CpuBarrier> cpu_barrier; |
| 286 | std::array<std::shared_ptr<Cpu>, NUM_CPU_CORES> cpu_cores; | 286 | std::array<std::shared_ptr<Cpu>, NUM_CPU_CORES> cpu_cores; |
| 287 | std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads; | 287 | std::array<std::unique_ptr<std::thread>, NUM_CPU_CORES - 1> cpu_core_threads; |
| 288 | size_t active_core{}; ///< Active core, only used in single thread mode | 288 | std::size_t active_core{}; ///< Active core, only used in single thread mode |
| 289 | 289 | ||
| 290 | /// Service manager | 290 | /// Service manager |
| 291 | std::shared_ptr<Service::SM::ServiceManager> service_manager; | 291 | std::shared_ptr<Service::SM::ServiceManager> service_manager; |
| @@ -348,7 +348,7 @@ ARM_Interface& System::CurrentArmInterface() { | |||
| 348 | return CurrentCpuCore().ArmInterface(); | 348 | return CurrentCpuCore().ArmInterface(); |
| 349 | } | 349 | } |
| 350 | 350 | ||
| 351 | size_t System::CurrentCoreIndex() { | 351 | std::size_t System::CurrentCoreIndex() { |
| 352 | return CurrentCpuCore().CoreIndex(); | 352 | return CurrentCpuCore().CoreIndex(); |
| 353 | } | 353 | } |
| 354 | 354 | ||
| @@ -356,7 +356,7 @@ Kernel::Scheduler& System::CurrentScheduler() { | |||
| 356 | return *CurrentCpuCore().Scheduler(); | 356 | return *CurrentCpuCore().Scheduler(); |
| 357 | } | 357 | } |
| 358 | 358 | ||
| 359 | const std::shared_ptr<Kernel::Scheduler>& System::Scheduler(size_t core_index) { | 359 | const std::shared_ptr<Kernel::Scheduler>& System::Scheduler(std::size_t core_index) { |
| 360 | ASSERT(core_index < NUM_CPU_CORES); | 360 | ASSERT(core_index < NUM_CPU_CORES); |
| 361 | return impl->cpu_cores[core_index]->Scheduler(); | 361 | return impl->cpu_cores[core_index]->Scheduler(); |
| 362 | } | 362 | } |
| @@ -369,12 +369,12 @@ const Kernel::SharedPtr<Kernel::Process>& System::CurrentProcess() const { | |||
| 369 | return impl->kernel.CurrentProcess(); | 369 | return impl->kernel.CurrentProcess(); |
| 370 | } | 370 | } |
| 371 | 371 | ||
| 372 | ARM_Interface& System::ArmInterface(size_t core_index) { | 372 | ARM_Interface& System::ArmInterface(std::size_t core_index) { |
| 373 | ASSERT(core_index < NUM_CPU_CORES); | 373 | ASSERT(core_index < NUM_CPU_CORES); |
| 374 | return impl->cpu_cores[core_index]->ArmInterface(); | 374 | return impl->cpu_cores[core_index]->ArmInterface(); |
| 375 | } | 375 | } |
| 376 | 376 | ||
| 377 | Cpu& System::CpuCore(size_t core_index) { | 377 | Cpu& System::CpuCore(std::size_t core_index) { |
| 378 | ASSERT(core_index < NUM_CPU_CORES); | 378 | ASSERT(core_index < NUM_CPU_CORES); |
| 379 | return *impl->cpu_cores[core_index]; | 379 | return *impl->cpu_cores[core_index]; |
| 380 | } | 380 | } |
diff --git a/src/core/core.h b/src/core/core.h index ab3663427..f9a3e97e3 100644 --- a/src/core/core.h +++ b/src/core/core.h | |||
| @@ -145,16 +145,16 @@ public: | |||
| 145 | ARM_Interface& CurrentArmInterface(); | 145 | ARM_Interface& CurrentArmInterface(); |
| 146 | 146 | ||
| 147 | /// Gets the index of the currently running CPU core | 147 | /// Gets the index of the currently running CPU core |
| 148 | size_t CurrentCoreIndex(); | 148 | std::size_t CurrentCoreIndex(); |
| 149 | 149 | ||
| 150 | /// Gets the scheduler for the CPU core that is currently running | 150 | /// Gets the scheduler for the CPU core that is currently running |
| 151 | Kernel::Scheduler& CurrentScheduler(); | 151 | Kernel::Scheduler& CurrentScheduler(); |
| 152 | 152 | ||
| 153 | /// Gets an ARM interface to the CPU core with the specified index | 153 | /// Gets an ARM interface to the CPU core with the specified index |
| 154 | ARM_Interface& ArmInterface(size_t core_index); | 154 | ARM_Interface& ArmInterface(std::size_t core_index); |
| 155 | 155 | ||
| 156 | /// Gets a CPU interface to the CPU core with the specified index | 156 | /// Gets a CPU interface to the CPU core with the specified index |
| 157 | Cpu& CpuCore(size_t core_index); | 157 | Cpu& CpuCore(std::size_t core_index); |
| 158 | 158 | ||
| 159 | /// Gets the exclusive monitor | 159 | /// Gets the exclusive monitor |
| 160 | ExclusiveMonitor& Monitor(); | 160 | ExclusiveMonitor& Monitor(); |
| @@ -172,7 +172,7 @@ public: | |||
| 172 | const VideoCore::RendererBase& Renderer() const; | 172 | const VideoCore::RendererBase& Renderer() const; |
| 173 | 173 | ||
| 174 | /// Gets the scheduler for the CPU core with the specified index | 174 | /// Gets the scheduler for the CPU core with the specified index |
| 175 | const std::shared_ptr<Kernel::Scheduler>& Scheduler(size_t core_index); | 175 | const std::shared_ptr<Kernel::Scheduler>& Scheduler(std::size_t core_index); |
| 176 | 176 | ||
| 177 | /// Provides a reference to the current process | 177 | /// Provides a reference to the current process |
| 178 | Kernel::SharedPtr<Kernel::Process>& CurrentProcess(); | 178 | Kernel::SharedPtr<Kernel::Process>& CurrentProcess(); |
diff --git a/src/core/core_cpu.cpp b/src/core/core_cpu.cpp index b042ee02b..265f8ed9c 100644 --- a/src/core/core_cpu.cpp +++ b/src/core/core_cpu.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #ifdef ARCHITECTURE_x86_64 | 9 | #ifdef ARCHITECTURE_x86_64 |
| 10 | #include "core/arm/dynarmic/arm_dynarmic.h" | 10 | #include "core/arm/dynarmic/arm_dynarmic.h" |
| 11 | #endif | 11 | #endif |
| 12 | #include "core/arm/exclusive_monitor.h" | ||
| 12 | #include "core/arm/unicorn/arm_unicorn.h" | 13 | #include "core/arm/unicorn/arm_unicorn.h" |
| 13 | #include "core/core_cpu.h" | 14 | #include "core/core_cpu.h" |
| 14 | #include "core/core_timing.h" | 15 | #include "core/core_timing.h" |
| @@ -49,24 +50,26 @@ bool CpuBarrier::Rendezvous() { | |||
| 49 | } | 50 | } |
| 50 | 51 | ||
| 51 | Cpu::Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, | 52 | Cpu::Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, |
| 52 | std::shared_ptr<CpuBarrier> cpu_barrier, size_t core_index) | 53 | std::shared_ptr<CpuBarrier> cpu_barrier, std::size_t core_index) |
| 53 | : cpu_barrier{std::move(cpu_barrier)}, core_index{core_index} { | 54 | : cpu_barrier{std::move(cpu_barrier)}, core_index{core_index} { |
| 54 | 55 | ||
| 55 | if (Settings::values.use_cpu_jit) { | 56 | if (Settings::values.use_cpu_jit) { |
| 56 | #ifdef ARCHITECTURE_x86_64 | 57 | #ifdef ARCHITECTURE_x86_64 |
| 57 | arm_interface = std::make_shared<ARM_Dynarmic>(exclusive_monitor, core_index); | 58 | arm_interface = std::make_unique<ARM_Dynarmic>(exclusive_monitor, core_index); |
| 58 | #else | 59 | #else |
| 59 | arm_interface = std::make_shared<ARM_Unicorn>(); | 60 | arm_interface = std::make_unique<ARM_Unicorn>(); |
| 60 | LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); | 61 | LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available"); |
| 61 | #endif | 62 | #endif |
| 62 | } else { | 63 | } else { |
| 63 | arm_interface = std::make_shared<ARM_Unicorn>(); | 64 | arm_interface = std::make_unique<ARM_Unicorn>(); |
| 64 | } | 65 | } |
| 65 | 66 | ||
| 66 | scheduler = std::make_shared<Kernel::Scheduler>(arm_interface.get()); | 67 | scheduler = std::make_shared<Kernel::Scheduler>(*arm_interface); |
| 67 | } | 68 | } |
| 68 | 69 | ||
| 69 | std::shared_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(size_t num_cores) { | 70 | Cpu::~Cpu() = default; |
| 71 | |||
| 72 | std::shared_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(std::size_t num_cores) { | ||
| 70 | if (Settings::values.use_cpu_jit) { | 73 | if (Settings::values.use_cpu_jit) { |
| 71 | #ifdef ARCHITECTURE_x86_64 | 74 | #ifdef ARCHITECTURE_x86_64 |
| 72 | return std::make_shared<DynarmicExclusiveMonitor>(num_cores); | 75 | return std::make_shared<DynarmicExclusiveMonitor>(num_cores); |
diff --git a/src/core/core_cpu.h b/src/core/core_cpu.h index 40ed34b47..ee7e04abc 100644 --- a/src/core/core_cpu.h +++ b/src/core/core_cpu.h | |||
| @@ -6,11 +6,10 @@ | |||
| 6 | 6 | ||
| 7 | #include <atomic> | 7 | #include <atomic> |
| 8 | #include <condition_variable> | 8 | #include <condition_variable> |
| 9 | #include <cstddef> | ||
| 9 | #include <memory> | 10 | #include <memory> |
| 10 | #include <mutex> | 11 | #include <mutex> |
| 11 | #include <string> | ||
| 12 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| 13 | #include "core/arm/exclusive_monitor.h" | ||
| 14 | 13 | ||
| 15 | namespace Kernel { | 14 | namespace Kernel { |
| 16 | class Scheduler; | 15 | class Scheduler; |
| @@ -19,6 +18,7 @@ class Scheduler; | |||
| 19 | namespace Core { | 18 | namespace Core { |
| 20 | 19 | ||
| 21 | class ARM_Interface; | 20 | class ARM_Interface; |
| 21 | class ExclusiveMonitor; | ||
| 22 | 22 | ||
| 23 | constexpr unsigned NUM_CPU_CORES{4}; | 23 | constexpr unsigned NUM_CPU_CORES{4}; |
| 24 | 24 | ||
| @@ -42,7 +42,8 @@ private: | |||
| 42 | class Cpu { | 42 | class Cpu { |
| 43 | public: | 43 | public: |
| 44 | Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, | 44 | Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, |
| 45 | std::shared_ptr<CpuBarrier> cpu_barrier, size_t core_index); | 45 | std::shared_ptr<CpuBarrier> cpu_barrier, std::size_t core_index); |
| 46 | ~Cpu(); | ||
| 46 | 47 | ||
| 47 | void RunLoop(bool tight_loop = true); | 48 | void RunLoop(bool tight_loop = true); |
| 48 | 49 | ||
| @@ -66,21 +67,21 @@ public: | |||
| 66 | return core_index == 0; | 67 | return core_index == 0; |
| 67 | } | 68 | } |
| 68 | 69 | ||
| 69 | size_t CoreIndex() const { | 70 | std::size_t CoreIndex() const { |
| 70 | return core_index; | 71 | return core_index; |
| 71 | } | 72 | } |
| 72 | 73 | ||
| 73 | static std::shared_ptr<ExclusiveMonitor> MakeExclusiveMonitor(size_t num_cores); | 74 | static std::shared_ptr<ExclusiveMonitor> MakeExclusiveMonitor(std::size_t num_cores); |
| 74 | 75 | ||
| 75 | private: | 76 | private: |
| 76 | void Reschedule(); | 77 | void Reschedule(); |
| 77 | 78 | ||
| 78 | std::shared_ptr<ARM_Interface> arm_interface; | 79 | std::unique_ptr<ARM_Interface> arm_interface; |
| 79 | std::shared_ptr<CpuBarrier> cpu_barrier; | 80 | std::shared_ptr<CpuBarrier> cpu_barrier; |
| 80 | std::shared_ptr<Kernel::Scheduler> scheduler; | 81 | std::shared_ptr<Kernel::Scheduler> scheduler; |
| 81 | 82 | ||
| 82 | std::atomic<bool> reschedule_pending = false; | 83 | std::atomic<bool> reschedule_pending = false; |
| 83 | size_t core_index; | 84 | std::size_t core_index; |
| 84 | }; | 85 | }; |
| 85 | 86 | ||
| 86 | } // namespace Core | 87 | } // namespace Core |
diff --git a/src/core/crypto/aes_util.cpp b/src/core/crypto/aes_util.cpp index 89ade5000..4be76bb43 100644 --- a/src/core/crypto/aes_util.cpp +++ b/src/core/crypto/aes_util.cpp | |||
| @@ -10,9 +10,9 @@ | |||
| 10 | 10 | ||
| 11 | namespace Core::Crypto { | 11 | namespace Core::Crypto { |
| 12 | namespace { | 12 | namespace { |
| 13 | std::vector<u8> CalculateNintendoTweak(size_t sector_id) { | 13 | std::vector<u8> CalculateNintendoTweak(std::size_t sector_id) { |
| 14 | std::vector<u8> out(0x10); | 14 | std::vector<u8> out(0x10); |
| 15 | for (size_t i = 0xF; i <= 0xF; --i) { | 15 | for (std::size_t i = 0xF; i <= 0xF; --i) { |
| 16 | out[i] = sector_id & 0xFF; | 16 | out[i] = sector_id & 0xFF; |
| 17 | sector_id >>= 8; | 17 | sector_id >>= 8; |
| 18 | } | 18 | } |
| @@ -20,11 +20,14 @@ std::vector<u8> CalculateNintendoTweak(size_t sector_id) { | |||
| 20 | } | 20 | } |
| 21 | } // Anonymous namespace | 21 | } // Anonymous namespace |
| 22 | 22 | ||
| 23 | static_assert(static_cast<size_t>(Mode::CTR) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_CTR), | 23 | static_assert(static_cast<std::size_t>(Mode::CTR) == |
| 24 | static_cast<std::size_t>(MBEDTLS_CIPHER_AES_128_CTR), | ||
| 24 | "CTR has incorrect value."); | 25 | "CTR has incorrect value."); |
| 25 | static_assert(static_cast<size_t>(Mode::ECB) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_ECB), | 26 | static_assert(static_cast<std::size_t>(Mode::ECB) == |
| 27 | static_cast<std::size_t>(MBEDTLS_CIPHER_AES_128_ECB), | ||
| 26 | "ECB has incorrect value."); | 28 | "ECB has incorrect value."); |
| 27 | static_assert(static_cast<size_t>(Mode::XTS) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_XTS), | 29 | static_assert(static_cast<std::size_t>(Mode::XTS) == |
| 30 | static_cast<std::size_t>(MBEDTLS_CIPHER_AES_128_XTS), | ||
| 28 | "XTS has incorrect value."); | 31 | "XTS has incorrect value."); |
| 29 | 32 | ||
| 30 | // Structure to hide mbedtls types from header file | 33 | // Structure to hide mbedtls types from header file |
| @@ -33,7 +36,7 @@ struct CipherContext { | |||
| 33 | mbedtls_cipher_context_t decryption_context; | 36 | mbedtls_cipher_context_t decryption_context; |
| 34 | }; | 37 | }; |
| 35 | 38 | ||
| 36 | template <typename Key, size_t KeySize> | 39 | template <typename Key, std::size_t KeySize> |
| 37 | Crypto::AESCipher<Key, KeySize>::AESCipher(Key key, Mode mode) | 40 | Crypto::AESCipher<Key, KeySize>::AESCipher(Key key, Mode mode) |
| 38 | : ctx(std::make_unique<CipherContext>()) { | 41 | : ctx(std::make_unique<CipherContext>()) { |
| 39 | mbedtls_cipher_init(&ctx->encryption_context); | 42 | mbedtls_cipher_init(&ctx->encryption_context); |
| @@ -54,26 +57,26 @@ Crypto::AESCipher<Key, KeySize>::AESCipher(Key key, Mode mode) | |||
| 54 | //"Failed to set key on mbedtls ciphers."); | 57 | //"Failed to set key on mbedtls ciphers."); |
| 55 | } | 58 | } |
| 56 | 59 | ||
| 57 | template <typename Key, size_t KeySize> | 60 | template <typename Key, std::size_t KeySize> |
| 58 | AESCipher<Key, KeySize>::~AESCipher() { | 61 | AESCipher<Key, KeySize>::~AESCipher() { |
| 59 | mbedtls_cipher_free(&ctx->encryption_context); | 62 | mbedtls_cipher_free(&ctx->encryption_context); |
| 60 | mbedtls_cipher_free(&ctx->decryption_context); | 63 | mbedtls_cipher_free(&ctx->decryption_context); |
| 61 | } | 64 | } |
| 62 | 65 | ||
| 63 | template <typename Key, size_t KeySize> | 66 | template <typename Key, std::size_t KeySize> |
| 64 | void AESCipher<Key, KeySize>::SetIV(std::vector<u8> iv) { | 67 | void AESCipher<Key, KeySize>::SetIV(std::vector<u8> iv) { |
| 65 | ASSERT_MSG((mbedtls_cipher_set_iv(&ctx->encryption_context, iv.data(), iv.size()) || | 68 | ASSERT_MSG((mbedtls_cipher_set_iv(&ctx->encryption_context, iv.data(), iv.size()) || |
| 66 | mbedtls_cipher_set_iv(&ctx->decryption_context, iv.data(), iv.size())) == 0, | 69 | mbedtls_cipher_set_iv(&ctx->decryption_context, iv.data(), iv.size())) == 0, |
| 67 | "Failed to set IV on mbedtls ciphers."); | 70 | "Failed to set IV on mbedtls ciphers."); |
| 68 | } | 71 | } |
| 69 | 72 | ||
| 70 | template <typename Key, size_t KeySize> | 73 | template <typename Key, std::size_t KeySize> |
| 71 | void AESCipher<Key, KeySize>::Transcode(const u8* src, size_t size, u8* dest, Op op) const { | 74 | void AESCipher<Key, KeySize>::Transcode(const u8* src, std::size_t size, u8* dest, Op op) const { |
| 72 | auto* const context = op == Op::Encrypt ? &ctx->encryption_context : &ctx->decryption_context; | 75 | auto* const context = op == Op::Encrypt ? &ctx->encryption_context : &ctx->decryption_context; |
| 73 | 76 | ||
| 74 | mbedtls_cipher_reset(context); | 77 | mbedtls_cipher_reset(context); |
| 75 | 78 | ||
| 76 | size_t written = 0; | 79 | std::size_t written = 0; |
| 77 | if (mbedtls_cipher_get_cipher_mode(context) == MBEDTLS_MODE_XTS) { | 80 | if (mbedtls_cipher_get_cipher_mode(context) == MBEDTLS_MODE_XTS) { |
| 78 | mbedtls_cipher_update(context, src, size, dest, &written); | 81 | mbedtls_cipher_update(context, src, size, dest, &written); |
| 79 | if (written != size) { | 82 | if (written != size) { |
| @@ -90,8 +93,8 @@ void AESCipher<Key, KeySize>::Transcode(const u8* src, size_t size, u8* dest, Op | |||
| 90 | return; | 93 | return; |
| 91 | } | 94 | } |
| 92 | 95 | ||
| 93 | for (size_t offset = 0; offset < size; offset += block_size) { | 96 | for (std::size_t offset = 0; offset < size; offset += block_size) { |
| 94 | auto length = std::min<size_t>(block_size, size - offset); | 97 | auto length = std::min<std::size_t>(block_size, size - offset); |
| 95 | mbedtls_cipher_update(context, src + offset, length, dest + offset, &written); | 98 | mbedtls_cipher_update(context, src + offset, length, dest + offset, &written); |
| 96 | if (written != length) { | 99 | if (written != length) { |
| 97 | if (length < block_size) { | 100 | if (length < block_size) { |
| @@ -110,12 +113,12 @@ void AESCipher<Key, KeySize>::Transcode(const u8* src, size_t size, u8* dest, Op | |||
| 110 | mbedtls_cipher_finish(context, nullptr, nullptr); | 113 | mbedtls_cipher_finish(context, nullptr, nullptr); |
| 111 | } | 114 | } |
| 112 | 115 | ||
| 113 | template <typename Key, size_t KeySize> | 116 | template <typename Key, std::size_t KeySize> |
| 114 | void AESCipher<Key, KeySize>::XTSTranscode(const u8* src, size_t size, u8* dest, size_t sector_id, | 117 | void AESCipher<Key, KeySize>::XTSTranscode(const u8* src, std::size_t size, u8* dest, |
| 115 | size_t sector_size, Op op) { | 118 | std::size_t sector_id, std::size_t sector_size, Op op) { |
| 116 | ASSERT_MSG(size % sector_size == 0, "XTS decryption size must be a multiple of sector size."); | 119 | ASSERT_MSG(size % sector_size == 0, "XTS decryption size must be a multiple of sector size."); |
| 117 | 120 | ||
| 118 | for (size_t i = 0; i < size; i += sector_size) { | 121 | for (std::size_t i = 0; i < size; i += sector_size) { |
| 119 | SetIV(CalculateNintendoTweak(sector_id++)); | 122 | SetIV(CalculateNintendoTweak(sector_id++)); |
| 120 | Transcode<u8, u8>(src + i, sector_size, dest + i, op); | 123 | Transcode<u8, u8>(src + i, sector_size, dest + i, op); |
| 121 | } | 124 | } |
diff --git a/src/core/crypto/aes_util.h b/src/core/crypto/aes_util.h index 8ce9d6612..edc4ab910 100644 --- a/src/core/crypto/aes_util.h +++ b/src/core/crypto/aes_util.h | |||
| @@ -25,7 +25,7 @@ enum class Op { | |||
| 25 | Decrypt, | 25 | Decrypt, |
| 26 | }; | 26 | }; |
| 27 | 27 | ||
| 28 | template <typename Key, size_t KeySize = sizeof(Key)> | 28 | template <typename Key, std::size_t KeySize = sizeof(Key)> |
| 29 | class AESCipher { | 29 | class AESCipher { |
| 30 | static_assert(std::is_same_v<Key, std::array<u8, KeySize>>, "Key must be std::array of u8."); | 30 | static_assert(std::is_same_v<Key, std::array<u8, KeySize>>, "Key must be std::array of u8."); |
| 31 | static_assert(KeySize == 0x10 || KeySize == 0x20, "KeySize must be 128 or 256."); | 31 | static_assert(KeySize == 0x10 || KeySize == 0x20, "KeySize must be 128 or 256."); |
| @@ -38,25 +38,25 @@ public: | |||
| 38 | void SetIV(std::vector<u8> iv); | 38 | void SetIV(std::vector<u8> iv); |
| 39 | 39 | ||
| 40 | template <typename Source, typename Dest> | 40 | template <typename Source, typename Dest> |
| 41 | void Transcode(const Source* src, size_t size, Dest* dest, Op op) const { | 41 | void Transcode(const Source* src, std::size_t size, Dest* dest, Op op) const { |
| 42 | static_assert(std::is_trivially_copyable_v<Source> && std::is_trivially_copyable_v<Dest>, | 42 | static_assert(std::is_trivially_copyable_v<Source> && std::is_trivially_copyable_v<Dest>, |
| 43 | "Transcode source and destination types must be trivially copyable."); | 43 | "Transcode source and destination types must be trivially copyable."); |
| 44 | Transcode(reinterpret_cast<const u8*>(src), size, reinterpret_cast<u8*>(dest), op); | 44 | Transcode(reinterpret_cast<const u8*>(src), size, reinterpret_cast<u8*>(dest), op); |
| 45 | } | 45 | } |
| 46 | 46 | ||
| 47 | void Transcode(const u8* src, size_t size, u8* dest, Op op) const; | 47 | void Transcode(const u8* src, std::size_t size, u8* dest, Op op) const; |
| 48 | 48 | ||
| 49 | template <typename Source, typename Dest> | 49 | template <typename Source, typename Dest> |
| 50 | void XTSTranscode(const Source* src, size_t size, Dest* dest, size_t sector_id, | 50 | void XTSTranscode(const Source* src, std::size_t size, Dest* dest, std::size_t sector_id, |
| 51 | size_t sector_size, Op op) { | 51 | std::size_t sector_size, Op op) { |
| 52 | static_assert(std::is_trivially_copyable_v<Source> && std::is_trivially_copyable_v<Dest>, | 52 | static_assert(std::is_trivially_copyable_v<Source> && std::is_trivially_copyable_v<Dest>, |
| 53 | "XTSTranscode source and destination types must be trivially copyable."); | 53 | "XTSTranscode source and destination types must be trivially copyable."); |
| 54 | XTSTranscode(reinterpret_cast<const u8*>(src), size, reinterpret_cast<u8*>(dest), sector_id, | 54 | XTSTranscode(reinterpret_cast<const u8*>(src), size, reinterpret_cast<u8*>(dest), sector_id, |
| 55 | sector_size, op); | 55 | sector_size, op); |
| 56 | } | 56 | } |
| 57 | 57 | ||
| 58 | void XTSTranscode(const u8* src, size_t size, u8* dest, size_t sector_id, size_t sector_size, | 58 | void XTSTranscode(const u8* src, std::size_t size, u8* dest, std::size_t sector_id, |
| 59 | Op op); | 59 | std::size_t sector_size, Op op); |
| 60 | 60 | ||
| 61 | private: | 61 | private: |
| 62 | std::unique_ptr<CipherContext> ctx; | 62 | std::unique_ptr<CipherContext> ctx; |
diff --git a/src/core/crypto/ctr_encryption_layer.cpp b/src/core/crypto/ctr_encryption_layer.cpp index 296fad419..902841c77 100644 --- a/src/core/crypto/ctr_encryption_layer.cpp +++ b/src/core/crypto/ctr_encryption_layer.cpp | |||
| @@ -8,11 +8,12 @@ | |||
| 8 | 8 | ||
| 9 | namespace Core::Crypto { | 9 | namespace Core::Crypto { |
| 10 | 10 | ||
| 11 | CTREncryptionLayer::CTREncryptionLayer(FileSys::VirtualFile base_, Key128 key_, size_t base_offset) | 11 | CTREncryptionLayer::CTREncryptionLayer(FileSys::VirtualFile base_, Key128 key_, |
| 12 | std::size_t base_offset) | ||
| 12 | : EncryptionLayer(std::move(base_)), base_offset(base_offset), cipher(key_, Mode::CTR), | 13 | : EncryptionLayer(std::move(base_)), base_offset(base_offset), cipher(key_, Mode::CTR), |
| 13 | iv(16, 0) {} | 14 | iv(16, 0) {} |
| 14 | 15 | ||
| 15 | size_t CTREncryptionLayer::Read(u8* data, size_t length, size_t offset) const { | 16 | std::size_t CTREncryptionLayer::Read(u8* data, std::size_t length, std::size_t offset) const { |
| 16 | if (length == 0) | 17 | if (length == 0) |
| 17 | return 0; | 18 | return 0; |
| 18 | 19 | ||
| @@ -28,7 +29,7 @@ size_t CTREncryptionLayer::Read(u8* data, size_t length, size_t offset) const { | |||
| 28 | std::vector<u8> block = base->ReadBytes(0x10, offset - sector_offset); | 29 | std::vector<u8> block = base->ReadBytes(0x10, offset - sector_offset); |
| 29 | UpdateIV(base_offset + offset - sector_offset); | 30 | UpdateIV(base_offset + offset - sector_offset); |
| 30 | cipher.Transcode(block.data(), block.size(), block.data(), Op::Decrypt); | 31 | cipher.Transcode(block.data(), block.size(), block.data(), Op::Decrypt); |
| 31 | size_t read = 0x10 - sector_offset; | 32 | std::size_t read = 0x10 - sector_offset; |
| 32 | 33 | ||
| 33 | if (length + sector_offset < 0x10) { | 34 | if (length + sector_offset < 0x10) { |
| 34 | std::memcpy(data, block.data() + sector_offset, std::min<u64>(length, read)); | 35 | std::memcpy(data, block.data() + sector_offset, std::min<u64>(length, read)); |
| @@ -43,9 +44,9 @@ void CTREncryptionLayer::SetIV(const std::vector<u8>& iv_) { | |||
| 43 | iv.assign(iv_.cbegin(), iv_.cbegin() + length); | 44 | iv.assign(iv_.cbegin(), iv_.cbegin() + length); |
| 44 | } | 45 | } |
| 45 | 46 | ||
| 46 | void CTREncryptionLayer::UpdateIV(size_t offset) const { | 47 | void CTREncryptionLayer::UpdateIV(std::size_t offset) const { |
| 47 | offset >>= 4; | 48 | offset >>= 4; |
| 48 | for (size_t i = 0; i < 8; ++i) { | 49 | for (std::size_t i = 0; i < 8; ++i) { |
| 49 | iv[16 - i - 1] = offset & 0xFF; | 50 | iv[16 - i - 1] = offset & 0xFF; |
| 50 | offset >>= 8; | 51 | offset >>= 8; |
| 51 | } | 52 | } |
diff --git a/src/core/crypto/ctr_encryption_layer.h b/src/core/crypto/ctr_encryption_layer.h index 11b8683c7..a7bf810f4 100644 --- a/src/core/crypto/ctr_encryption_layer.h +++ b/src/core/crypto/ctr_encryption_layer.h | |||
| @@ -14,20 +14,20 @@ namespace Core::Crypto { | |||
| 14 | // Sits on top of a VirtualFile and provides CTR-mode AES decription. | 14 | // Sits on top of a VirtualFile and provides CTR-mode AES decription. |
| 15 | class CTREncryptionLayer : public EncryptionLayer { | 15 | class CTREncryptionLayer : public EncryptionLayer { |
| 16 | public: | 16 | public: |
| 17 | CTREncryptionLayer(FileSys::VirtualFile base, Key128 key, size_t base_offset); | 17 | CTREncryptionLayer(FileSys::VirtualFile base, Key128 key, std::size_t base_offset); |
| 18 | 18 | ||
| 19 | size_t Read(u8* data, size_t length, size_t offset) const override; | 19 | std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; |
| 20 | 20 | ||
| 21 | void SetIV(const std::vector<u8>& iv); | 21 | void SetIV(const std::vector<u8>& iv); |
| 22 | 22 | ||
| 23 | private: | 23 | private: |
| 24 | size_t base_offset; | 24 | std::size_t base_offset; |
| 25 | 25 | ||
| 26 | // Must be mutable as operations modify cipher contexts. | 26 | // Must be mutable as operations modify cipher contexts. |
| 27 | mutable AESCipher<Key128> cipher; | 27 | mutable AESCipher<Key128> cipher; |
| 28 | mutable std::vector<u8> iv; | 28 | mutable std::vector<u8> iv; |
| 29 | 29 | ||
| 30 | void UpdateIV(size_t offset) const; | 30 | void UpdateIV(std::size_t offset) const; |
| 31 | }; | 31 | }; |
| 32 | 32 | ||
| 33 | } // namespace Core::Crypto | 33 | } // namespace Core::Crypto |
diff --git a/src/core/crypto/encryption_layer.cpp b/src/core/crypto/encryption_layer.cpp index 4204527e3..4c377d7d4 100644 --- a/src/core/crypto/encryption_layer.cpp +++ b/src/core/crypto/encryption_layer.cpp | |||
| @@ -12,11 +12,11 @@ std::string EncryptionLayer::GetName() const { | |||
| 12 | return base->GetName(); | 12 | return base->GetName(); |
| 13 | } | 13 | } |
| 14 | 14 | ||
| 15 | size_t EncryptionLayer::GetSize() const { | 15 | std::size_t EncryptionLayer::GetSize() const { |
| 16 | return base->GetSize(); | 16 | return base->GetSize(); |
| 17 | } | 17 | } |
| 18 | 18 | ||
| 19 | bool EncryptionLayer::Resize(size_t new_size) { | 19 | bool EncryptionLayer::Resize(std::size_t new_size) { |
| 20 | return false; | 20 | return false; |
| 21 | } | 21 | } |
| 22 | 22 | ||
| @@ -32,7 +32,7 @@ bool EncryptionLayer::IsReadable() const { | |||
| 32 | return true; | 32 | return true; |
| 33 | } | 33 | } |
| 34 | 34 | ||
| 35 | size_t EncryptionLayer::Write(const u8* data, size_t length, size_t offset) { | 35 | std::size_t EncryptionLayer::Write(const u8* data, std::size_t length, std::size_t offset) { |
| 36 | return 0; | 36 | return 0; |
| 37 | } | 37 | } |
| 38 | 38 | ||
diff --git a/src/core/crypto/encryption_layer.h b/src/core/crypto/encryption_layer.h index 7f05af9b4..53619cb38 100644 --- a/src/core/crypto/encryption_layer.h +++ b/src/core/crypto/encryption_layer.h | |||
| @@ -15,15 +15,15 @@ class EncryptionLayer : public FileSys::VfsFile { | |||
| 15 | public: | 15 | public: |
| 16 | explicit EncryptionLayer(FileSys::VirtualFile base); | 16 | explicit EncryptionLayer(FileSys::VirtualFile base); |
| 17 | 17 | ||
| 18 | size_t Read(u8* data, size_t length, size_t offset) const override = 0; | 18 | std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override = 0; |
| 19 | 19 | ||
| 20 | std::string GetName() const override; | 20 | std::string GetName() const override; |
| 21 | size_t GetSize() const override; | 21 | std::size_t GetSize() const override; |
| 22 | bool Resize(size_t new_size) override; | 22 | bool Resize(std::size_t new_size) override; |
| 23 | std::shared_ptr<FileSys::VfsDirectory> GetContainingDirectory() const override; | 23 | std::shared_ptr<FileSys::VfsDirectory> GetContainingDirectory() const override; |
| 24 | bool IsWritable() const override; | 24 | bool IsWritable() const override; |
| 25 | bool IsReadable() const override; | 25 | bool IsReadable() const override; |
| 26 | size_t Write(const u8* data, size_t length, size_t offset) override; | 26 | std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override; |
| 27 | bool Rename(std::string_view name) override; | 27 | bool Rename(std::string_view name) override; |
| 28 | 28 | ||
| 29 | protected: | 29 | protected: |
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp index 6f27f990b..bf3a70944 100644 --- a/src/core/crypto/key_manager.cpp +++ b/src/core/crypto/key_manager.cpp | |||
| @@ -54,7 +54,7 @@ boost::optional<Key128> DeriveSDSeed() { | |||
| 54 | return boost::none; | 54 | return boost::none; |
| 55 | 55 | ||
| 56 | std::array<u8, 0x10> buffer{}; | 56 | std::array<u8, 0x10> buffer{}; |
| 57 | size_t offset = 0; | 57 | std::size_t offset = 0; |
| 58 | for (; offset + 0x10 < save_43.GetSize(); ++offset) { | 58 | for (; offset + 0x10 < save_43.GetSize(); ++offset) { |
| 59 | save_43.Seek(offset, SEEK_SET); | 59 | save_43.Seek(offset, SEEK_SET); |
| 60 | save_43.ReadBytes(buffer.data(), buffer.size()); | 60 | save_43.ReadBytes(buffer.data(), buffer.size()); |
| @@ -105,7 +105,7 @@ Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, const KeyManag | |||
| 105 | 105 | ||
| 106 | // Combine sources and seed | 106 | // Combine sources and seed |
| 107 | for (auto& source : sd_key_sources) { | 107 | for (auto& source : sd_key_sources) { |
| 108 | for (size_t i = 0; i < source.size(); ++i) | 108 | for (std::size_t i = 0; i < source.size(); ++i) |
| 109 | source[i] ^= sd_seed[i & 0xF]; | 109 | source[i] ^= sd_seed[i & 0xF]; |
| 110 | } | 110 | } |
| 111 | 111 | ||
| @@ -207,7 +207,7 @@ Key256 KeyManager::GetKey(S256KeyType id, u64 field1, u64 field2) const { | |||
| 207 | return s256_keys.at({id, field1, field2}); | 207 | return s256_keys.at({id, field1, field2}); |
| 208 | } | 208 | } |
| 209 | 209 | ||
| 210 | template <size_t Size> | 210 | template <std::size_t Size> |
| 211 | void KeyManager::WriteKeyToFile(bool title_key, std::string_view keyname, | 211 | void KeyManager::WriteKeyToFile(bool title_key, std::string_view keyname, |
| 212 | const std::array<u8, Size>& key) { | 212 | const std::array<u8, Size>& key) { |
| 213 | const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir); | 213 | const std::string yuzu_keys_dir = FileUtil::GetUserPath(FileUtil::UserPath::KeysDir); |
diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h index ce67913bb..978eec8dc 100644 --- a/src/core/crypto/key_manager.h +++ b/src/core/crypto/key_manager.h | |||
| @@ -108,7 +108,7 @@ private: | |||
| 108 | void LoadFromFile(const std::string& filename, bool is_title_keys); | 108 | void LoadFromFile(const std::string& filename, bool is_title_keys); |
| 109 | void AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2, | 109 | void AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2, |
| 110 | const std::string& filename, bool title); | 110 | const std::string& filename, bool title); |
| 111 | template <size_t Size> | 111 | template <std::size_t Size> |
| 112 | void WriteKeyToFile(bool title_key, std::string_view keyname, const std::array<u8, Size>& key); | 112 | void WriteKeyToFile(bool title_key, std::string_view keyname, const std::array<u8, Size>& key); |
| 113 | 113 | ||
| 114 | static const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> s128_file_id; | 114 | static const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> s128_file_id; |
diff --git a/src/core/crypto/xts_encryption_layer.cpp b/src/core/crypto/xts_encryption_layer.cpp index c10832cfe..8f0ba4ee7 100644 --- a/src/core/crypto/xts_encryption_layer.cpp +++ b/src/core/crypto/xts_encryption_layer.cpp | |||
| @@ -14,7 +14,7 @@ constexpr u64 XTS_SECTOR_SIZE = 0x4000; | |||
| 14 | XTSEncryptionLayer::XTSEncryptionLayer(FileSys::VirtualFile base_, Key256 key_) | 14 | XTSEncryptionLayer::XTSEncryptionLayer(FileSys::VirtualFile base_, Key256 key_) |
| 15 | : EncryptionLayer(std::move(base_)), cipher(key_, Mode::XTS) {} | 15 | : EncryptionLayer(std::move(base_)), cipher(key_, Mode::XTS) {} |
| 16 | 16 | ||
| 17 | size_t XTSEncryptionLayer::Read(u8* data, size_t length, size_t offset) const { | 17 | std::size_t XTSEncryptionLayer::Read(u8* data, std::size_t length, std::size_t offset) const { |
| 18 | if (length == 0) | 18 | if (length == 0) |
| 19 | return 0; | 19 | return 0; |
| 20 | 20 | ||
| @@ -46,7 +46,7 @@ size_t XTSEncryptionLayer::Read(u8* data, size_t length, size_t offset) const { | |||
| 46 | block.resize(XTS_SECTOR_SIZE); | 46 | block.resize(XTS_SECTOR_SIZE); |
| 47 | cipher.XTSTranscode(block.data(), block.size(), block.data(), | 47 | cipher.XTSTranscode(block.data(), block.size(), block.data(), |
| 48 | (offset - sector_offset) / XTS_SECTOR_SIZE, XTS_SECTOR_SIZE, Op::Decrypt); | 48 | (offset - sector_offset) / XTS_SECTOR_SIZE, XTS_SECTOR_SIZE, Op::Decrypt); |
| 49 | const size_t read = XTS_SECTOR_SIZE - sector_offset; | 49 | const std::size_t read = XTS_SECTOR_SIZE - sector_offset; |
| 50 | 50 | ||
| 51 | if (length + sector_offset < XTS_SECTOR_SIZE) { | 51 | if (length + sector_offset < XTS_SECTOR_SIZE) { |
| 52 | std::memcpy(data, block.data() + sector_offset, std::min<u64>(length, read)); | 52 | std::memcpy(data, block.data() + sector_offset, std::min<u64>(length, read)); |
diff --git a/src/core/crypto/xts_encryption_layer.h b/src/core/crypto/xts_encryption_layer.h index 7a1f1dc64..5f8f00fe7 100644 --- a/src/core/crypto/xts_encryption_layer.h +++ b/src/core/crypto/xts_encryption_layer.h | |||
| @@ -15,7 +15,7 @@ class XTSEncryptionLayer : public EncryptionLayer { | |||
| 15 | public: | 15 | public: |
| 16 | XTSEncryptionLayer(FileSys::VirtualFile base, Key256 key); | 16 | XTSEncryptionLayer(FileSys::VirtualFile base, Key256 key); |
| 17 | 17 | ||
| 18 | size_t Read(u8* data, size_t length, size_t offset) const override; | 18 | std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; |
| 19 | 19 | ||
| 20 | private: | 20 | private: |
| 21 | // Must be mutable as operations modify cipher contexts. | 21 | // Must be mutable as operations modify cipher contexts. |
diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp index 205492897..6102ef476 100644 --- a/src/core/file_sys/bis_factory.cpp +++ b/src/core/file_sys/bis_factory.cpp | |||
| @@ -2,13 +2,14 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <fmt/format.h> | ||
| 5 | #include "core/file_sys/bis_factory.h" | 6 | #include "core/file_sys/bis_factory.h" |
| 6 | #include "core/file_sys/registered_cache.h" | 7 | #include "core/file_sys/registered_cache.h" |
| 7 | 8 | ||
| 8 | namespace FileSys { | 9 | namespace FileSys { |
| 9 | 10 | ||
| 10 | BISFactory::BISFactory(VirtualDir nand_root_) | 11 | BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_) |
| 11 | : nand_root(std::move(nand_root_)), | 12 | : nand_root(std::move(nand_root_)), load_root(std::move(load_root_)), |
| 12 | sysnand_cache(std::make_shared<RegisteredCache>( | 13 | sysnand_cache(std::make_shared<RegisteredCache>( |
| 13 | GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))), | 14 | GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))), |
| 14 | usrnand_cache(std::make_shared<RegisteredCache>( | 15 | usrnand_cache(std::make_shared<RegisteredCache>( |
| @@ -24,4 +25,11 @@ std::shared_ptr<RegisteredCache> BISFactory::GetUserNANDContents() const { | |||
| 24 | return usrnand_cache; | 25 | return usrnand_cache; |
| 25 | } | 26 | } |
| 26 | 27 | ||
| 28 | VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const { | ||
| 29 | // LayeredFS doesn't work on updates and title id-less homebrew | ||
| 30 | if (title_id == 0 || (title_id & 0x800) > 0) | ||
| 31 | return nullptr; | ||
| 32 | return GetOrCreateDirectoryRelative(load_root, fmt::format("/{:016X}", title_id)); | ||
| 33 | } | ||
| 34 | |||
| 27 | } // namespace FileSys | 35 | } // namespace FileSys |
diff --git a/src/core/file_sys/bis_factory.h b/src/core/file_sys/bis_factory.h index 9523dd864..c352e0925 100644 --- a/src/core/file_sys/bis_factory.h +++ b/src/core/file_sys/bis_factory.h | |||
| @@ -17,14 +17,17 @@ class RegisteredCache; | |||
| 17 | /// registered caches. | 17 | /// registered caches. |
| 18 | class BISFactory { | 18 | class BISFactory { |
| 19 | public: | 19 | public: |
| 20 | explicit BISFactory(VirtualDir nand_root); | 20 | explicit BISFactory(VirtualDir nand_root, VirtualDir load_root); |
| 21 | ~BISFactory(); | 21 | ~BISFactory(); |
| 22 | 22 | ||
| 23 | std::shared_ptr<RegisteredCache> GetSystemNANDContents() const; | 23 | std::shared_ptr<RegisteredCache> GetSystemNANDContents() const; |
| 24 | std::shared_ptr<RegisteredCache> GetUserNANDContents() const; | 24 | std::shared_ptr<RegisteredCache> GetUserNANDContents() const; |
| 25 | 25 | ||
| 26 | VirtualDir GetModificationLoadRoot(u64 title_id) const; | ||
| 27 | |||
| 26 | private: | 28 | private: |
| 27 | VirtualDir nand_root; | 29 | VirtualDir nand_root; |
| 30 | VirtualDir load_root; | ||
| 28 | 31 | ||
| 29 | std::shared_ptr<RegisteredCache> sysnand_cache; | 32 | std::shared_ptr<RegisteredCache> sysnand_cache; |
| 30 | std::shared_ptr<RegisteredCache> usrnand_cache; | 33 | std::shared_ptr<RegisteredCache> usrnand_cache; |
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp index 8218893b2..edfc1bbd4 100644 --- a/src/core/file_sys/card_image.cpp +++ b/src/core/file_sys/card_image.cpp | |||
| @@ -41,13 +41,14 @@ XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) { | |||
| 41 | 41 | ||
| 42 | for (XCIPartition partition : | 42 | for (XCIPartition partition : |
| 43 | {XCIPartition::Update, XCIPartition::Normal, XCIPartition::Secure, XCIPartition::Logo}) { | 43 | {XCIPartition::Update, XCIPartition::Normal, XCIPartition::Secure, XCIPartition::Logo}) { |
| 44 | auto raw = main_hfs.GetFile(partition_names[static_cast<size_t>(partition)]); | 44 | auto raw = main_hfs.GetFile(partition_names[static_cast<std::size_t>(partition)]); |
| 45 | if (raw != nullptr) | 45 | if (raw != nullptr) |
| 46 | partitions[static_cast<size_t>(partition)] = std::make_shared<PartitionFilesystem>(raw); | 46 | partitions[static_cast<std::size_t>(partition)] = |
| 47 | std::make_shared<PartitionFilesystem>(raw); | ||
| 47 | } | 48 | } |
| 48 | 49 | ||
| 49 | secure_partition = std::make_shared<NSP>( | 50 | secure_partition = std::make_shared<NSP>( |
| 50 | main_hfs.GetFile(partition_names[static_cast<size_t>(XCIPartition::Secure)])); | 51 | main_hfs.GetFile(partition_names[static_cast<std::size_t>(XCIPartition::Secure)])); |
| 51 | 52 | ||
| 52 | const auto secure_ncas = secure_partition->GetNCAsCollapsed(); | 53 | const auto secure_ncas = secure_partition->GetNCAsCollapsed(); |
| 53 | std::copy(secure_ncas.begin(), secure_ncas.end(), std::back_inserter(ncas)); | 54 | std::copy(secure_ncas.begin(), secure_ncas.end(), std::back_inserter(ncas)); |
| @@ -92,7 +93,7 @@ Loader::ResultStatus XCI::GetProgramNCAStatus() const { | |||
| 92 | } | 93 | } |
| 93 | 94 | ||
| 94 | VirtualDir XCI::GetPartition(XCIPartition partition) const { | 95 | VirtualDir XCI::GetPartition(XCIPartition partition) const { |
| 95 | return partitions[static_cast<size_t>(partition)]; | 96 | return partitions[static_cast<std::size_t>(partition)]; |
| 96 | } | 97 | } |
| 97 | 98 | ||
| 98 | std::shared_ptr<NSP> XCI::GetSecurePartitionNSP() const { | 99 | std::shared_ptr<NSP> XCI::GetSecurePartitionNSP() const { |
| @@ -168,11 +169,11 @@ bool XCI::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { | |||
| 168 | } | 169 | } |
| 169 | 170 | ||
| 170 | Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) { | 171 | Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) { |
| 171 | if (partitions[static_cast<size_t>(part)] == nullptr) { | 172 | if (partitions[static_cast<std::size_t>(part)] == nullptr) { |
| 172 | return Loader::ResultStatus::ErrorXCIMissingPartition; | 173 | return Loader::ResultStatus::ErrorXCIMissingPartition; |
| 173 | } | 174 | } |
| 174 | 175 | ||
| 175 | for (const VirtualFile& file : partitions[static_cast<size_t>(part)]->GetFiles()) { | 176 | for (const VirtualFile& file : partitions[static_cast<std::size_t>(part)]->GetFiles()) { |
| 176 | if (file->GetExtension() != "nca") | 177 | if (file->GetExtension() != "nca") |
| 177 | continue; | 178 | continue; |
| 178 | auto nca = std::make_shared<NCA>(file); | 179 | auto nca = std::make_shared<NCA>(file); |
| @@ -187,7 +188,7 @@ Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) { | |||
| 187 | } else { | 188 | } else { |
| 188 | const u16 error_id = static_cast<u16>(nca->GetStatus()); | 189 | const u16 error_id = static_cast<u16>(nca->GetStatus()); |
| 189 | LOG_CRITICAL(Loader, "Could not load NCA {}/{}, failed with error code {:04X} ({})", | 190 | LOG_CRITICAL(Loader, "Could not load NCA {}/{}, failed with error code {:04X} ({})", |
| 190 | partition_names[static_cast<size_t>(part)], nca->GetName(), error_id, | 191 | partition_names[static_cast<std::size_t>(part)], nca->GetName(), error_id, |
| 191 | nca->GetStatus()); | 192 | nca->GetStatus()); |
| 192 | } | 193 | } |
| 193 | } | 194 | } |
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index 79bfb6fec..aa1b3c17d 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp | |||
| @@ -298,11 +298,11 @@ NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_off | |||
| 298 | auto section = sections[i]; | 298 | auto section = sections[i]; |
| 299 | 299 | ||
| 300 | if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) { | 300 | if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) { |
| 301 | const size_t base_offset = | 301 | const std::size_t base_offset = |
| 302 | header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER; | 302 | header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER; |
| 303 | ivfc_offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset; | 303 | ivfc_offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset; |
| 304 | const size_t romfs_offset = base_offset + ivfc_offset; | 304 | const std::size_t romfs_offset = base_offset + ivfc_offset; |
| 305 | const size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size; | 305 | const std::size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size; |
| 306 | auto raw = std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset); | 306 | auto raw = std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset); |
| 307 | auto dec = Decrypt(section, raw, romfs_offset); | 307 | auto dec = Decrypt(section, raw, romfs_offset); |
| 308 | 308 | ||
| @@ -463,6 +463,8 @@ NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_off | |||
| 463 | status = Loader::ResultStatus::Success; | 463 | status = Loader::ResultStatus::Success; |
| 464 | } | 464 | } |
| 465 | 465 | ||
| 466 | NCA::~NCA() = default; | ||
| 467 | |||
| 466 | Loader::ResultStatus NCA::GetStatus() const { | 468 | Loader::ResultStatus NCA::GetStatus() const { |
| 467 | return status; | 469 | return status; |
| 468 | } | 470 | } |
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h index 00eca52da..f9f66cae9 100644 --- a/src/core/file_sys/content_archive.h +++ b/src/core/file_sys/content_archive.h | |||
| @@ -81,6 +81,8 @@ class NCA : public ReadOnlyVfsDirectory { | |||
| 81 | public: | 81 | public: |
| 82 | explicit NCA(VirtualFile file, VirtualFile bktr_base_romfs = nullptr, | 82 | explicit NCA(VirtualFile file, VirtualFile bktr_base_romfs = nullptr, |
| 83 | u64 bktr_base_ivfc_offset = 0); | 83 | u64 bktr_base_ivfc_offset = 0); |
| 84 | ~NCA() override; | ||
| 85 | |||
| 84 | Loader::ResultStatus GetStatus() const; | 86 | Loader::ResultStatus GetStatus() const; |
| 85 | 87 | ||
| 86 | std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; | 88 | std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; |
diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp index e76bf77bf..5b1177a03 100644 --- a/src/core/file_sys/control_metadata.cpp +++ b/src/core/file_sys/control_metadata.cpp | |||
| @@ -8,6 +8,14 @@ | |||
| 8 | 8 | ||
| 9 | namespace FileSys { | 9 | namespace FileSys { |
| 10 | 10 | ||
| 11 | const std::array<const char*, 15> LANGUAGE_NAMES = { | ||
| 12 | "AmericanEnglish", "BritishEnglish", "Japanese", | ||
| 13 | "French", "German", "LatinAmericanSpanish", | ||
| 14 | "Spanish", "Italian", "Dutch", | ||
| 15 | "CanadianFrench", "Portugese", "Russian", | ||
| 16 | "Korean", "Taiwanese", "Chinese", | ||
| 17 | }; | ||
| 18 | |||
| 11 | std::string LanguageEntry::GetApplicationName() const { | 19 | std::string LanguageEntry::GetApplicationName() const { |
| 12 | return Common::StringFromFixedZeroTerminatedBuffer(application_name.data(), 0x200); | 20 | return Common::StringFromFixedZeroTerminatedBuffer(application_name.data(), 0x200); |
| 13 | } | 21 | } |
| @@ -20,18 +28,20 @@ NACP::NACP(VirtualFile file) : raw(std::make_unique<RawNACP>()) { | |||
| 20 | file->ReadObject(raw.get()); | 28 | file->ReadObject(raw.get()); |
| 21 | } | 29 | } |
| 22 | 30 | ||
| 31 | NACP::~NACP() = default; | ||
| 32 | |||
| 23 | const LanguageEntry& NACP::GetLanguageEntry(Language language) const { | 33 | const LanguageEntry& NACP::GetLanguageEntry(Language language) const { |
| 24 | if (language != Language::Default) { | 34 | if (language != Language::Default) { |
| 25 | return raw->language_entries.at(static_cast<u8>(language)); | 35 | return raw->language_entries.at(static_cast<u8>(language)); |
| 26 | } else { | ||
| 27 | for (const auto& language_entry : raw->language_entries) { | ||
| 28 | if (!language_entry.GetApplicationName().empty()) | ||
| 29 | return language_entry; | ||
| 30 | } | ||
| 31 | |||
| 32 | // Fallback to English | ||
| 33 | return GetLanguageEntry(Language::AmericanEnglish); | ||
| 34 | } | 36 | } |
| 37 | |||
| 38 | for (const auto& language_entry : raw->language_entries) { | ||
| 39 | if (!language_entry.GetApplicationName().empty()) | ||
| 40 | return language_entry; | ||
| 41 | } | ||
| 42 | |||
| 43 | // Fallback to English | ||
| 44 | return GetLanguageEntry(Language::AmericanEnglish); | ||
| 35 | } | 45 | } |
| 36 | 46 | ||
| 37 | std::string NACP::GetApplicationName(Language language) const { | 47 | std::string NACP::GetApplicationName(Language language) const { |
diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h index 8a510bf46..43d6f0719 100644 --- a/src/core/file_sys/control_metadata.h +++ b/src/core/file_sys/control_metadata.h | |||
| @@ -66,18 +66,15 @@ enum class Language : u8 { | |||
| 66 | Default = 255, | 66 | Default = 255, |
| 67 | }; | 67 | }; |
| 68 | 68 | ||
| 69 | static constexpr std::array<const char*, 15> LANGUAGE_NAMES = { | 69 | extern const std::array<const char*, 15> LANGUAGE_NAMES; |
| 70 | "AmericanEnglish", "BritishEnglish", "Japanese", | ||
| 71 | "French", "German", "LatinAmericanSpanish", | ||
| 72 | "Spanish", "Italian", "Dutch", | ||
| 73 | "CanadianFrench", "Portugese", "Russian", | ||
| 74 | "Korean", "Taiwanese", "Chinese"}; | ||
| 75 | 70 | ||
| 76 | // A class representing the format used by NX metadata files, typically named Control.nacp. | 71 | // A class representing the format used by NX metadata files, typically named Control.nacp. |
| 77 | // These store application name, dev name, title id, and other miscellaneous data. | 72 | // These store application name, dev name, title id, and other miscellaneous data. |
| 78 | class NACP { | 73 | class NACP { |
| 79 | public: | 74 | public: |
| 80 | explicit NACP(VirtualFile file); | 75 | explicit NACP(VirtualFile file); |
| 76 | ~NACP(); | ||
| 77 | |||
| 81 | const LanguageEntry& GetLanguageEntry(Language language = Language::Default) const; | 78 | const LanguageEntry& GetLanguageEntry(Language language = Language::Default) const; |
| 82 | std::string GetApplicationName(Language language = Language::Default) const; | 79 | std::string GetApplicationName(Language language = Language::Default) const; |
| 83 | std::string GetDeveloperName(Language language = Language::Default) const; | 80 | std::string GetDeveloperName(Language language = Language::Default) const; |
diff --git a/src/core/file_sys/directory.h b/src/core/file_sys/directory.h index 3759e743a..12bb90ec8 100644 --- a/src/core/file_sys/directory.h +++ b/src/core/file_sys/directory.h | |||
| @@ -25,7 +25,7 @@ enum EntryType : u8 { | |||
| 25 | struct Entry { | 25 | struct Entry { |
| 26 | Entry(std::string_view view, EntryType entry_type, u64 entry_size) | 26 | Entry(std::string_view view, EntryType entry_type, u64 entry_size) |
| 27 | : type{entry_type}, file_size{entry_size} { | 27 | : type{entry_type}, file_size{entry_size} { |
| 28 | const size_t copy_size = view.copy(filename, std::size(filename) - 1); | 28 | const std::size_t copy_size = view.copy(filename, std::size(filename) - 1); |
| 29 | filename[copy_size] = '\0'; | 29 | filename[copy_size] = '\0'; |
| 30 | } | 30 | } |
| 31 | 31 | ||
diff --git a/src/core/file_sys/fsmitm_romfsbuild.cpp b/src/core/file_sys/fsmitm_romfsbuild.cpp new file mode 100644 index 000000000..2a913ce82 --- /dev/null +++ b/src/core/file_sys/fsmitm_romfsbuild.cpp | |||
| @@ -0,0 +1,366 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2018 Atmosphère-NX | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify it | ||
| 5 | * under the terms and conditions of the GNU General Public License, | ||
| 6 | * version 2, as published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 11 | * more details. | ||
| 12 | * | ||
| 13 | * You should have received a copy of the GNU General Public License | ||
| 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 15 | */ | ||
| 16 | |||
| 17 | /* | ||
| 18 | * Adapted by DarkLordZach for use/interaction with yuzu | ||
| 19 | * | ||
| 20 | * Modifications Copyright 2018 yuzu emulator team | ||
| 21 | * Licensed under GPLv2 or any later version | ||
| 22 | * Refer to the license.txt file included. | ||
| 23 | */ | ||
| 24 | |||
| 25 | #include <cstring> | ||
| 26 | #include "common/alignment.h" | ||
| 27 | #include "common/assert.h" | ||
| 28 | #include "core/file_sys/fsmitm_romfsbuild.h" | ||
| 29 | #include "core/file_sys/vfs.h" | ||
| 30 | #include "core/file_sys/vfs_vector.h" | ||
| 31 | |||
| 32 | namespace FileSys { | ||
| 33 | |||
| 34 | constexpr u64 FS_MAX_PATH = 0x301; | ||
| 35 | |||
| 36 | constexpr u32 ROMFS_ENTRY_EMPTY = 0xFFFFFFFF; | ||
| 37 | constexpr u32 ROMFS_FILEPARTITION_OFS = 0x200; | ||
| 38 | |||
| 39 | // Types for building a RomFS. | ||
| 40 | struct RomFSHeader { | ||
| 41 | u64 header_size; | ||
| 42 | u64 dir_hash_table_ofs; | ||
| 43 | u64 dir_hash_table_size; | ||
| 44 | u64 dir_table_ofs; | ||
| 45 | u64 dir_table_size; | ||
| 46 | u64 file_hash_table_ofs; | ||
| 47 | u64 file_hash_table_size; | ||
| 48 | u64 file_table_ofs; | ||
| 49 | u64 file_table_size; | ||
| 50 | u64 file_partition_ofs; | ||
| 51 | }; | ||
| 52 | static_assert(sizeof(RomFSHeader) == 0x50, "RomFSHeader has incorrect size."); | ||
| 53 | |||
| 54 | struct RomFSDirectoryEntry { | ||
| 55 | u32 parent; | ||
| 56 | u32 sibling; | ||
| 57 | u32 child; | ||
| 58 | u32 file; | ||
| 59 | u32 hash; | ||
| 60 | u32 name_size; | ||
| 61 | }; | ||
| 62 | static_assert(sizeof(RomFSDirectoryEntry) == 0x18, "RomFSDirectoryEntry has incorrect size."); | ||
| 63 | |||
| 64 | struct RomFSFileEntry { | ||
| 65 | u32 parent; | ||
| 66 | u32 sibling; | ||
| 67 | u64 offset; | ||
| 68 | u64 size; | ||
| 69 | u32 hash; | ||
| 70 | u32 name_size; | ||
| 71 | }; | ||
| 72 | static_assert(sizeof(RomFSFileEntry) == 0x20, "RomFSFileEntry has incorrect size."); | ||
| 73 | |||
| 74 | struct RomFSBuildFileContext; | ||
| 75 | |||
| 76 | struct RomFSBuildDirectoryContext { | ||
| 77 | std::string path; | ||
| 78 | u32 cur_path_ofs = 0; | ||
| 79 | u32 path_len = 0; | ||
| 80 | u32 entry_offset = 0; | ||
| 81 | std::shared_ptr<RomFSBuildDirectoryContext> parent; | ||
| 82 | std::shared_ptr<RomFSBuildDirectoryContext> child; | ||
| 83 | std::shared_ptr<RomFSBuildDirectoryContext> sibling; | ||
| 84 | std::shared_ptr<RomFSBuildFileContext> file; | ||
| 85 | }; | ||
| 86 | |||
| 87 | struct RomFSBuildFileContext { | ||
| 88 | std::string path; | ||
| 89 | u32 cur_path_ofs = 0; | ||
| 90 | u32 path_len = 0; | ||
| 91 | u32 entry_offset = 0; | ||
| 92 | u64 offset = 0; | ||
| 93 | u64 size = 0; | ||
| 94 | std::shared_ptr<RomFSBuildDirectoryContext> parent; | ||
| 95 | std::shared_ptr<RomFSBuildFileContext> sibling; | ||
| 96 | VirtualFile source; | ||
| 97 | }; | ||
| 98 | |||
| 99 | static u32 romfs_calc_path_hash(u32 parent, std::string path, u32 start, std::size_t path_len) { | ||
| 100 | u32 hash = parent ^ 123456789; | ||
| 101 | for (u32 i = 0; i < path_len; i++) { | ||
| 102 | hash = (hash >> 5) | (hash << 27); | ||
| 103 | hash ^= path[start + i]; | ||
| 104 | } | ||
| 105 | |||
| 106 | return hash; | ||
| 107 | } | ||
| 108 | |||
| 109 | static u64 romfs_get_hash_table_count(u64 num_entries) { | ||
| 110 | if (num_entries < 3) { | ||
| 111 | return 3; | ||
| 112 | } | ||
| 113 | |||
| 114 | if (num_entries < 19) { | ||
| 115 | return num_entries | 1; | ||
| 116 | } | ||
| 117 | |||
| 118 | u64 count = num_entries; | ||
| 119 | while (count % 2 == 0 || count % 3 == 0 || count % 5 == 0 || count % 7 == 0 || | ||
| 120 | count % 11 == 0 || count % 13 == 0 || count % 17 == 0) { | ||
| 121 | count++; | ||
| 122 | } | ||
| 123 | return count; | ||
| 124 | } | ||
| 125 | |||
| 126 | void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs, | ||
| 127 | std::shared_ptr<RomFSBuildDirectoryContext> parent) { | ||
| 128 | std::vector<std::shared_ptr<RomFSBuildDirectoryContext>> child_dirs; | ||
| 129 | |||
| 130 | VirtualDir dir; | ||
| 131 | |||
| 132 | if (parent->path_len == 0) | ||
| 133 | dir = root_romfs; | ||
| 134 | else | ||
| 135 | dir = root_romfs->GetDirectoryRelative(parent->path); | ||
| 136 | |||
| 137 | const auto entries = dir->GetEntries(); | ||
| 138 | |||
| 139 | for (const auto& kv : entries) { | ||
| 140 | if (kv.second == VfsEntryType::Directory) { | ||
| 141 | const auto child = std::make_shared<RomFSBuildDirectoryContext>(); | ||
| 142 | // Set child's path. | ||
| 143 | child->cur_path_ofs = parent->path_len + 1; | ||
| 144 | child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size()); | ||
| 145 | child->path = parent->path + "/" + kv.first; | ||
| 146 | |||
| 147 | // Sanity check on path_len | ||
| 148 | ASSERT(child->path_len < FS_MAX_PATH); | ||
| 149 | |||
| 150 | if (AddDirectory(parent, child)) { | ||
| 151 | child_dirs.push_back(child); | ||
| 152 | } | ||
| 153 | } else { | ||
| 154 | const auto child = std::make_shared<RomFSBuildFileContext>(); | ||
| 155 | // Set child's path. | ||
| 156 | child->cur_path_ofs = parent->path_len + 1; | ||
| 157 | child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size()); | ||
| 158 | child->path = parent->path + "/" + kv.first; | ||
| 159 | |||
| 160 | // Sanity check on path_len | ||
| 161 | ASSERT(child->path_len < FS_MAX_PATH); | ||
| 162 | |||
| 163 | child->source = root_romfs->GetFileRelative(child->path); | ||
| 164 | |||
| 165 | child->size = child->source->GetSize(); | ||
| 166 | |||
| 167 | AddFile(parent, child); | ||
| 168 | } | ||
| 169 | } | ||
| 170 | |||
| 171 | for (auto& child : child_dirs) { | ||
| 172 | this->VisitDirectory(root_romfs, child); | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | bool RomFSBuildContext::AddDirectory(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx, | ||
| 177 | std::shared_ptr<RomFSBuildDirectoryContext> dir_ctx) { | ||
| 178 | // Check whether it's already in the known directories. | ||
| 179 | const auto existing = directories.find(dir_ctx->path); | ||
| 180 | if (existing != directories.end()) | ||
| 181 | return false; | ||
| 182 | |||
| 183 | // Add a new directory. | ||
| 184 | num_dirs++; | ||
| 185 | dir_table_size += | ||
| 186 | sizeof(RomFSDirectoryEntry) + Common::AlignUp(dir_ctx->path_len - dir_ctx->cur_path_ofs, 4); | ||
| 187 | dir_ctx->parent = parent_dir_ctx; | ||
| 188 | directories.emplace(dir_ctx->path, dir_ctx); | ||
| 189 | |||
| 190 | return true; | ||
| 191 | } | ||
| 192 | |||
| 193 | bool RomFSBuildContext::AddFile(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx, | ||
| 194 | std::shared_ptr<RomFSBuildFileContext> file_ctx) { | ||
| 195 | // Check whether it's already in the known files. | ||
| 196 | const auto existing = files.find(file_ctx->path); | ||
| 197 | if (existing != files.end()) { | ||
| 198 | return false; | ||
| 199 | } | ||
| 200 | |||
| 201 | // Add a new file. | ||
| 202 | num_files++; | ||
| 203 | file_table_size += | ||
| 204 | sizeof(RomFSFileEntry) + Common::AlignUp(file_ctx->path_len - file_ctx->cur_path_ofs, 4); | ||
| 205 | file_ctx->parent = parent_dir_ctx; | ||
| 206 | files.emplace(file_ctx->path, file_ctx); | ||
| 207 | |||
| 208 | return true; | ||
| 209 | } | ||
| 210 | |||
| 211 | RomFSBuildContext::RomFSBuildContext(VirtualDir base_) : base(std::move(base_)) { | ||
| 212 | root = std::make_shared<RomFSBuildDirectoryContext>(); | ||
| 213 | root->path = "\0"; | ||
| 214 | directories.emplace(root->path, root); | ||
| 215 | num_dirs = 1; | ||
| 216 | dir_table_size = 0x18; | ||
| 217 | |||
| 218 | VisitDirectory(base, root); | ||
| 219 | } | ||
| 220 | |||
| 221 | RomFSBuildContext::~RomFSBuildContext() = default; | ||
| 222 | |||
| 223 | std::map<u64, VirtualFile> RomFSBuildContext::Build() { | ||
| 224 | const u64 dir_hash_table_entry_count = romfs_get_hash_table_count(num_dirs); | ||
| 225 | const u64 file_hash_table_entry_count = romfs_get_hash_table_count(num_files); | ||
| 226 | dir_hash_table_size = 4 * dir_hash_table_entry_count; | ||
| 227 | file_hash_table_size = 4 * file_hash_table_entry_count; | ||
| 228 | |||
| 229 | // Assign metadata pointers | ||
| 230 | RomFSHeader header{}; | ||
| 231 | |||
| 232 | std::vector<u32> dir_hash_table(dir_hash_table_entry_count, ROMFS_ENTRY_EMPTY); | ||
| 233 | std::vector<u32> file_hash_table(file_hash_table_entry_count, ROMFS_ENTRY_EMPTY); | ||
| 234 | |||
| 235 | std::vector<u8> dir_table(dir_table_size); | ||
| 236 | std::vector<u8> file_table(file_table_size); | ||
| 237 | |||
| 238 | std::shared_ptr<RomFSBuildFileContext> cur_file; | ||
| 239 | |||
| 240 | // Determine file offsets. | ||
| 241 | u32 entry_offset = 0; | ||
| 242 | std::shared_ptr<RomFSBuildFileContext> prev_file = nullptr; | ||
| 243 | for (const auto& it : files) { | ||
| 244 | cur_file = it.second; | ||
| 245 | file_partition_size = Common::AlignUp(file_partition_size, 16); | ||
| 246 | cur_file->offset = file_partition_size; | ||
| 247 | file_partition_size += cur_file->size; | ||
| 248 | cur_file->entry_offset = entry_offset; | ||
| 249 | entry_offset += sizeof(RomFSFileEntry) + | ||
| 250 | Common::AlignUp(cur_file->path_len - cur_file->cur_path_ofs, 4); | ||
| 251 | prev_file = cur_file; | ||
| 252 | } | ||
| 253 | // Assign deferred parent/sibling ownership. | ||
| 254 | for (auto it = files.rbegin(); it != files.rend(); ++it) { | ||
| 255 | cur_file = it->second; | ||
| 256 | cur_file->sibling = cur_file->parent->file; | ||
| 257 | cur_file->parent->file = cur_file; | ||
| 258 | } | ||
| 259 | |||
| 260 | std::shared_ptr<RomFSBuildDirectoryContext> cur_dir; | ||
| 261 | |||
| 262 | // Determine directory offsets. | ||
| 263 | entry_offset = 0; | ||
| 264 | for (const auto& it : directories) { | ||
| 265 | cur_dir = it.second; | ||
| 266 | cur_dir->entry_offset = entry_offset; | ||
| 267 | entry_offset += sizeof(RomFSDirectoryEntry) + | ||
| 268 | Common::AlignUp(cur_dir->path_len - cur_dir->cur_path_ofs, 4); | ||
| 269 | } | ||
| 270 | // Assign deferred parent/sibling ownership. | ||
| 271 | for (auto it = directories.rbegin(); it->second != root; ++it) { | ||
| 272 | cur_dir = it->second; | ||
| 273 | cur_dir->sibling = cur_dir->parent->child; | ||
| 274 | cur_dir->parent->child = cur_dir; | ||
| 275 | } | ||
| 276 | |||
| 277 | std::map<u64, VirtualFile> out; | ||
| 278 | |||
| 279 | // Populate file tables. | ||
| 280 | for (const auto& it : files) { | ||
| 281 | cur_file = it.second; | ||
| 282 | RomFSFileEntry cur_entry{}; | ||
| 283 | |||
| 284 | cur_entry.parent = cur_file->parent->entry_offset; | ||
| 285 | cur_entry.sibling = | ||
| 286 | cur_file->sibling == nullptr ? ROMFS_ENTRY_EMPTY : cur_file->sibling->entry_offset; | ||
| 287 | cur_entry.offset = cur_file->offset; | ||
| 288 | cur_entry.size = cur_file->size; | ||
| 289 | |||
| 290 | const auto name_size = cur_file->path_len - cur_file->cur_path_ofs; | ||
| 291 | const auto hash = romfs_calc_path_hash(cur_file->parent->entry_offset, cur_file->path, | ||
| 292 | cur_file->cur_path_ofs, name_size); | ||
| 293 | cur_entry.hash = file_hash_table[hash % file_hash_table_entry_count]; | ||
| 294 | file_hash_table[hash % file_hash_table_entry_count] = cur_file->entry_offset; | ||
| 295 | |||
| 296 | cur_entry.name_size = name_size; | ||
| 297 | |||
| 298 | out.emplace(cur_file->offset + ROMFS_FILEPARTITION_OFS, cur_file->source); | ||
| 299 | std::memcpy(file_table.data() + cur_file->entry_offset, &cur_entry, sizeof(RomFSFileEntry)); | ||
| 300 | std::memset(file_table.data() + cur_file->entry_offset + sizeof(RomFSFileEntry), 0, | ||
| 301 | Common::AlignUp(cur_entry.name_size, 4)); | ||
| 302 | std::memcpy(file_table.data() + cur_file->entry_offset + sizeof(RomFSFileEntry), | ||
| 303 | cur_file->path.data() + cur_file->cur_path_ofs, name_size); | ||
| 304 | } | ||
| 305 | |||
| 306 | // Populate dir tables. | ||
| 307 | for (const auto& it : directories) { | ||
| 308 | cur_dir = it.second; | ||
| 309 | RomFSDirectoryEntry cur_entry{}; | ||
| 310 | |||
| 311 | cur_entry.parent = cur_dir == root ? 0 : cur_dir->parent->entry_offset; | ||
| 312 | cur_entry.sibling = | ||
| 313 | cur_dir->sibling == nullptr ? ROMFS_ENTRY_EMPTY : cur_dir->sibling->entry_offset; | ||
| 314 | cur_entry.child = | ||
| 315 | cur_dir->child == nullptr ? ROMFS_ENTRY_EMPTY : cur_dir->child->entry_offset; | ||
| 316 | cur_entry.file = cur_dir->file == nullptr ? ROMFS_ENTRY_EMPTY : cur_dir->file->entry_offset; | ||
| 317 | |||
| 318 | const auto name_size = cur_dir->path_len - cur_dir->cur_path_ofs; | ||
| 319 | const auto hash = romfs_calc_path_hash(cur_dir == root ? 0 : cur_dir->parent->entry_offset, | ||
| 320 | cur_dir->path, cur_dir->cur_path_ofs, name_size); | ||
| 321 | cur_entry.hash = dir_hash_table[hash % dir_hash_table_entry_count]; | ||
| 322 | dir_hash_table[hash % dir_hash_table_entry_count] = cur_dir->entry_offset; | ||
| 323 | |||
| 324 | cur_entry.name_size = name_size; | ||
| 325 | |||
| 326 | std::memcpy(dir_table.data() + cur_dir->entry_offset, &cur_entry, | ||
| 327 | sizeof(RomFSDirectoryEntry)); | ||
| 328 | std::memset(dir_table.data() + cur_dir->entry_offset + sizeof(RomFSDirectoryEntry), 0, | ||
| 329 | Common::AlignUp(cur_entry.name_size, 4)); | ||
| 330 | std::memcpy(dir_table.data() + cur_dir->entry_offset + sizeof(RomFSDirectoryEntry), | ||
| 331 | cur_dir->path.data() + cur_dir->cur_path_ofs, name_size); | ||
| 332 | } | ||
| 333 | |||
| 334 | // Set header fields. | ||
| 335 | header.header_size = sizeof(RomFSHeader); | ||
| 336 | header.file_hash_table_size = file_hash_table_size; | ||
| 337 | header.file_table_size = file_table_size; | ||
| 338 | header.dir_hash_table_size = dir_hash_table_size; | ||
| 339 | header.dir_table_size = dir_table_size; | ||
| 340 | header.file_partition_ofs = ROMFS_FILEPARTITION_OFS; | ||
| 341 | header.dir_hash_table_ofs = Common::AlignUp(header.file_partition_ofs + file_partition_size, 4); | ||
| 342 | header.dir_table_ofs = header.dir_hash_table_ofs + header.dir_hash_table_size; | ||
| 343 | header.file_hash_table_ofs = header.dir_table_ofs + header.dir_table_size; | ||
| 344 | header.file_table_ofs = header.file_hash_table_ofs + header.file_hash_table_size; | ||
| 345 | |||
| 346 | std::vector<u8> header_data(sizeof(RomFSHeader)); | ||
| 347 | std::memcpy(header_data.data(), &header, header_data.size()); | ||
| 348 | out.emplace(0, std::make_shared<VectorVfsFile>(std::move(header_data))); | ||
| 349 | |||
| 350 | std::vector<u8> metadata(file_hash_table_size + file_table_size + dir_hash_table_size + | ||
| 351 | dir_table_size); | ||
| 352 | std::size_t index = 0; | ||
| 353 | std::memcpy(metadata.data(), dir_hash_table.data(), dir_hash_table.size() * sizeof(u32)); | ||
| 354 | index += dir_hash_table.size() * sizeof(u32); | ||
| 355 | std::memcpy(metadata.data() + index, dir_table.data(), dir_table.size()); | ||
| 356 | index += dir_table.size(); | ||
| 357 | std::memcpy(metadata.data() + index, file_hash_table.data(), | ||
| 358 | file_hash_table.size() * sizeof(u32)); | ||
| 359 | index += file_hash_table.size() * sizeof(u32); | ||
| 360 | std::memcpy(metadata.data() + index, file_table.data(), file_table.size()); | ||
| 361 | out.emplace(header.dir_hash_table_ofs, std::make_shared<VectorVfsFile>(std::move(metadata))); | ||
| 362 | |||
| 363 | return out; | ||
| 364 | } | ||
| 365 | |||
| 366 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/fsmitm_romfsbuild.h b/src/core/file_sys/fsmitm_romfsbuild.h new file mode 100644 index 000000000..b0c3c123b --- /dev/null +++ b/src/core/file_sys/fsmitm_romfsbuild.h | |||
| @@ -0,0 +1,70 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2018 Atmosphère-NX | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify it | ||
| 5 | * under the terms and conditions of the GNU General Public License, | ||
| 6 | * version 2, as published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 11 | * more details. | ||
| 12 | * | ||
| 13 | * You should have received a copy of the GNU General Public License | ||
| 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 15 | */ | ||
| 16 | |||
| 17 | /* | ||
| 18 | * Adapted by DarkLordZach for use/interaction with yuzu | ||
| 19 | * | ||
| 20 | * Modifications Copyright 2018 yuzu emulator team | ||
| 21 | * Licensed under GPLv2 or any later version | ||
| 22 | * Refer to the license.txt file included. | ||
| 23 | */ | ||
| 24 | |||
| 25 | #pragma once | ||
| 26 | |||
| 27 | #include <map> | ||
| 28 | #include <memory> | ||
| 29 | #include <string> | ||
| 30 | #include <boost/detail/container_fwd.hpp> | ||
| 31 | #include "common/common_types.h" | ||
| 32 | #include "core/file_sys/vfs.h" | ||
| 33 | |||
| 34 | namespace FileSys { | ||
| 35 | |||
| 36 | struct RomFSBuildDirectoryContext; | ||
| 37 | struct RomFSBuildFileContext; | ||
| 38 | struct RomFSDirectoryEntry; | ||
| 39 | struct RomFSFileEntry; | ||
| 40 | |||
| 41 | class RomFSBuildContext { | ||
| 42 | public: | ||
| 43 | explicit RomFSBuildContext(VirtualDir base); | ||
| 44 | ~RomFSBuildContext(); | ||
| 45 | |||
| 46 | // This finalizes the context. | ||
| 47 | std::map<u64, VirtualFile> Build(); | ||
| 48 | |||
| 49 | private: | ||
| 50 | VirtualDir base; | ||
| 51 | std::shared_ptr<RomFSBuildDirectoryContext> root; | ||
| 52 | std::map<std::string, std::shared_ptr<RomFSBuildDirectoryContext>, std::less<>> directories; | ||
| 53 | std::map<std::string, std::shared_ptr<RomFSBuildFileContext>, std::less<>> files; | ||
| 54 | u64 num_dirs = 0; | ||
| 55 | u64 num_files = 0; | ||
| 56 | u64 dir_table_size = 0; | ||
| 57 | u64 file_table_size = 0; | ||
| 58 | u64 dir_hash_table_size = 0; | ||
| 59 | u64 file_hash_table_size = 0; | ||
| 60 | u64 file_partition_size = 0; | ||
| 61 | |||
| 62 | void VisitDirectory(VirtualDir filesys, std::shared_ptr<RomFSBuildDirectoryContext> parent); | ||
| 63 | |||
| 64 | bool AddDirectory(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx, | ||
| 65 | std::shared_ptr<RomFSBuildDirectoryContext> dir_ctx); | ||
| 66 | bool AddFile(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx, | ||
| 67 | std::shared_ptr<RomFSBuildFileContext> file_ctx); | ||
| 68 | }; | ||
| 69 | |||
| 70 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/nca_metadata.cpp b/src/core/file_sys/nca_metadata.cpp index cdfbc5aaf..6f34b7836 100644 --- a/src/core/file_sys/nca_metadata.cpp +++ b/src/core/file_sys/nca_metadata.cpp | |||
| @@ -11,11 +11,11 @@ | |||
| 11 | namespace FileSys { | 11 | namespace FileSys { |
| 12 | 12 | ||
| 13 | bool operator>=(TitleType lhs, TitleType rhs) { | 13 | bool operator>=(TitleType lhs, TitleType rhs) { |
| 14 | return static_cast<size_t>(lhs) >= static_cast<size_t>(rhs); | 14 | return static_cast<std::size_t>(lhs) >= static_cast<std::size_t>(rhs); |
| 15 | } | 15 | } |
| 16 | 16 | ||
| 17 | bool operator<=(TitleType lhs, TitleType rhs) { | 17 | bool operator<=(TitleType lhs, TitleType rhs) { |
| 18 | return static_cast<size_t>(lhs) <= static_cast<size_t>(rhs); | 18 | return static_cast<std::size_t>(lhs) <= static_cast<std::size_t>(rhs); |
| 19 | } | 19 | } |
| 20 | 20 | ||
| 21 | CNMT::CNMT(VirtualFile file) { | 21 | CNMT::CNMT(VirtualFile file) { |
| @@ -51,6 +51,8 @@ CNMT::CNMT(CNMTHeader header, OptionalHeader opt_header, std::vector<ContentReco | |||
| 51 | : header(std::move(header)), opt_header(std::move(opt_header)), | 51 | : header(std::move(header)), opt_header(std::move(opt_header)), |
| 52 | content_records(std::move(content_records)), meta_records(std::move(meta_records)) {} | 52 | content_records(std::move(content_records)), meta_records(std::move(meta_records)) {} |
| 53 | 53 | ||
| 54 | CNMT::~CNMT() = default; | ||
| 55 | |||
| 54 | u64 CNMT::GetTitleID() const { | 56 | u64 CNMT::GetTitleID() const { |
| 55 | return header.title_id; | 57 | return header.title_id; |
| 56 | } | 58 | } |
diff --git a/src/core/file_sys/nca_metadata.h b/src/core/file_sys/nca_metadata.h index da5a8dbe8..a05d155f4 100644 --- a/src/core/file_sys/nca_metadata.h +++ b/src/core/file_sys/nca_metadata.h | |||
| @@ -87,6 +87,7 @@ public: | |||
| 87 | explicit CNMT(VirtualFile file); | 87 | explicit CNMT(VirtualFile file); |
| 88 | CNMT(CNMTHeader header, OptionalHeader opt_header, std::vector<ContentRecord> content_records, | 88 | CNMT(CNMTHeader header, OptionalHeader opt_header, std::vector<ContentRecord> content_records, |
| 89 | std::vector<MetaRecord> meta_records); | 89 | std::vector<MetaRecord> meta_records); |
| 90 | ~CNMT(); | ||
| 90 | 91 | ||
| 91 | u64 GetTitleID() const; | 92 | u64 GetTitleID() const; |
| 92 | u32 GetTitleVersion() const; | 93 | u32 GetTitleVersion() const; |
diff --git a/src/core/file_sys/nca_patch.cpp b/src/core/file_sys/nca_patch.cpp index 6fc5bd7d8..0090cc6c4 100644 --- a/src/core/file_sys/nca_patch.cpp +++ b/src/core/file_sys/nca_patch.cpp | |||
| @@ -22,11 +22,11 @@ BKTR::BKTR(VirtualFile base_romfs_, VirtualFile bktr_romfs_, RelocationBlock rel | |||
| 22 | base_romfs(std::move(base_romfs_)), bktr_romfs(std::move(bktr_romfs_)), | 22 | base_romfs(std::move(base_romfs_)), bktr_romfs(std::move(bktr_romfs_)), |
| 23 | encrypted(is_encrypted_), key(key_), base_offset(base_offset_), ivfc_offset(ivfc_offset_), | 23 | encrypted(is_encrypted_), key(key_), base_offset(base_offset_), ivfc_offset(ivfc_offset_), |
| 24 | section_ctr(section_ctr_) { | 24 | section_ctr(section_ctr_) { |
| 25 | for (size_t i = 0; i < relocation.number_buckets - 1; ++i) { | 25 | for (std::size_t i = 0; i < relocation.number_buckets - 1; ++i) { |
| 26 | relocation_buckets[i].entries.push_back({relocation.base_offsets[i + 1], 0, 0}); | 26 | relocation_buckets[i].entries.push_back({relocation.base_offsets[i + 1], 0, 0}); |
| 27 | } | 27 | } |
| 28 | 28 | ||
| 29 | for (size_t i = 0; i < subsection.number_buckets - 1; ++i) { | 29 | for (std::size_t i = 0; i < subsection.number_buckets - 1; ++i) { |
| 30 | subsection_buckets[i].entries.push_back({subsection_buckets[i + 1].entries[0].address_patch, | 30 | subsection_buckets[i].entries.push_back({subsection_buckets[i + 1].entries[0].address_patch, |
| 31 | {0}, | 31 | {0}, |
| 32 | subsection_buckets[i + 1].entries[0].ctr}); | 32 | subsection_buckets[i + 1].entries[0].ctr}); |
| @@ -37,7 +37,7 @@ BKTR::BKTR(VirtualFile base_romfs_, VirtualFile bktr_romfs_, RelocationBlock rel | |||
| 37 | 37 | ||
| 38 | BKTR::~BKTR() = default; | 38 | BKTR::~BKTR() = default; |
| 39 | 39 | ||
| 40 | size_t BKTR::Read(u8* data, size_t length, size_t offset) const { | 40 | std::size_t BKTR::Read(u8* data, std::size_t length, std::size_t offset) const { |
| 41 | // Read out of bounds. | 41 | // Read out of bounds. |
| 42 | if (offset >= relocation.size) | 42 | if (offset >= relocation.size) |
| 43 | return 0; | 43 | return 0; |
| @@ -69,14 +69,14 @@ size_t BKTR::Read(u8* data, size_t length, size_t offset) const { | |||
| 69 | std::vector<u8> iv(16); | 69 | std::vector<u8> iv(16); |
| 70 | auto subsection_ctr = subsection.ctr; | 70 | auto subsection_ctr = subsection.ctr; |
| 71 | auto offset_iv = section_offset + base_offset; | 71 | auto offset_iv = section_offset + base_offset; |
| 72 | for (size_t i = 0; i < section_ctr.size(); ++i) | 72 | for (std::size_t i = 0; i < section_ctr.size(); ++i) |
| 73 | iv[i] = section_ctr[0x8 - i - 1]; | 73 | iv[i] = section_ctr[0x8 - i - 1]; |
| 74 | offset_iv >>= 4; | 74 | offset_iv >>= 4; |
| 75 | for (size_t i = 0; i < sizeof(u64); ++i) { | 75 | for (std::size_t i = 0; i < sizeof(u64); ++i) { |
| 76 | iv[0xF - i] = static_cast<u8>(offset_iv & 0xFF); | 76 | iv[0xF - i] = static_cast<u8>(offset_iv & 0xFF); |
| 77 | offset_iv >>= 8; | 77 | offset_iv >>= 8; |
| 78 | } | 78 | } |
| 79 | for (size_t i = 0; i < sizeof(u32); ++i) { | 79 | for (std::size_t i = 0; i < sizeof(u32); ++i) { |
| 80 | iv[0x7 - i] = static_cast<u8>(subsection_ctr & 0xFF); | 80 | iv[0x7 - i] = static_cast<u8>(subsection_ctr & 0xFF); |
| 81 | subsection_ctr >>= 8; | 81 | subsection_ctr >>= 8; |
| 82 | } | 82 | } |
| @@ -110,8 +110,8 @@ size_t BKTR::Read(u8* data, size_t length, size_t offset) const { | |||
| 110 | } | 110 | } |
| 111 | 111 | ||
| 112 | template <bool Subsection, typename BlockType, typename BucketType> | 112 | template <bool Subsection, typename BlockType, typename BucketType> |
| 113 | std::pair<size_t, size_t> BKTR::SearchBucketEntry(u64 offset, BlockType block, | 113 | std::pair<std::size_t, std::size_t> BKTR::SearchBucketEntry(u64 offset, BlockType block, |
| 114 | BucketType buckets) const { | 114 | BucketType buckets) const { |
| 115 | if constexpr (Subsection) { | 115 | if constexpr (Subsection) { |
| 116 | const auto last_bucket = buckets[block.number_buckets - 1]; | 116 | const auto last_bucket = buckets[block.number_buckets - 1]; |
| 117 | if (offset >= last_bucket.entries[last_bucket.number_entries].address_patch) | 117 | if (offset >= last_bucket.entries[last_bucket.number_entries].address_patch) |
| @@ -120,18 +120,18 @@ std::pair<size_t, size_t> BKTR::SearchBucketEntry(u64 offset, BlockType block, | |||
| 120 | ASSERT_MSG(offset <= block.size, "Offset is out of bounds in BKTR relocation block."); | 120 | ASSERT_MSG(offset <= block.size, "Offset is out of bounds in BKTR relocation block."); |
| 121 | } | 121 | } |
| 122 | 122 | ||
| 123 | size_t bucket_id = std::count_if(block.base_offsets.begin() + 1, | 123 | std::size_t bucket_id = std::count_if( |
| 124 | block.base_offsets.begin() + block.number_buckets, | 124 | block.base_offsets.begin() + 1, block.base_offsets.begin() + block.number_buckets, |
| 125 | [&offset](u64 base_offset) { return base_offset <= offset; }); | 125 | [&offset](u64 base_offset) { return base_offset <= offset; }); |
| 126 | 126 | ||
| 127 | const auto bucket = buckets[bucket_id]; | 127 | const auto bucket = buckets[bucket_id]; |
| 128 | 128 | ||
| 129 | if (bucket.number_entries == 1) | 129 | if (bucket.number_entries == 1) |
| 130 | return {bucket_id, 0}; | 130 | return {bucket_id, 0}; |
| 131 | 131 | ||
| 132 | size_t low = 0; | 132 | std::size_t low = 0; |
| 133 | size_t mid = 0; | 133 | std::size_t mid = 0; |
| 134 | size_t high = bucket.number_entries - 1; | 134 | std::size_t high = bucket.number_entries - 1; |
| 135 | while (low <= high) { | 135 | while (low <= high) { |
| 136 | mid = (low + high) / 2; | 136 | mid = (low + high) / 2; |
| 137 | if (bucket.entries[mid].address_patch > offset) { | 137 | if (bucket.entries[mid].address_patch > offset) { |
| @@ -179,11 +179,11 @@ std::string BKTR::GetName() const { | |||
| 179 | return base_romfs->GetName(); | 179 | return base_romfs->GetName(); |
| 180 | } | 180 | } |
| 181 | 181 | ||
| 182 | size_t BKTR::GetSize() const { | 182 | std::size_t BKTR::GetSize() const { |
| 183 | return relocation.size; | 183 | return relocation.size; |
| 184 | } | 184 | } |
| 185 | 185 | ||
| 186 | bool BKTR::Resize(size_t new_size) { | 186 | bool BKTR::Resize(std::size_t new_size) { |
| 187 | return false; | 187 | return false; |
| 188 | } | 188 | } |
| 189 | 189 | ||
| @@ -199,7 +199,7 @@ bool BKTR::IsReadable() const { | |||
| 199 | return true; | 199 | return true; |
| 200 | } | 200 | } |
| 201 | 201 | ||
| 202 | size_t BKTR::Write(const u8* data, size_t length, size_t offset) { | 202 | std::size_t BKTR::Write(const u8* data, std::size_t length, std::size_t offset) { |
| 203 | return 0; | 203 | return 0; |
| 204 | } | 204 | } |
| 205 | 205 | ||
diff --git a/src/core/file_sys/nca_patch.h b/src/core/file_sys/nca_patch.h index 381f3504f..8e64e8378 100644 --- a/src/core/file_sys/nca_patch.h +++ b/src/core/file_sys/nca_patch.h | |||
| @@ -98,13 +98,13 @@ public: | |||
| 98 | Core::Crypto::Key128 key, u64 base_offset, u64 ivfc_offset, std::array<u8, 8> section_ctr); | 98 | Core::Crypto::Key128 key, u64 base_offset, u64 ivfc_offset, std::array<u8, 8> section_ctr); |
| 99 | ~BKTR() override; | 99 | ~BKTR() override; |
| 100 | 100 | ||
| 101 | size_t Read(u8* data, size_t length, size_t offset) const override; | 101 | std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; |
| 102 | 102 | ||
| 103 | std::string GetName() const override; | 103 | std::string GetName() const override; |
| 104 | 104 | ||
| 105 | size_t GetSize() const override; | 105 | std::size_t GetSize() const override; |
| 106 | 106 | ||
| 107 | bool Resize(size_t new_size) override; | 107 | bool Resize(std::size_t new_size) override; |
| 108 | 108 | ||
| 109 | std::shared_ptr<VfsDirectory> GetContainingDirectory() const override; | 109 | std::shared_ptr<VfsDirectory> GetContainingDirectory() const override; |
| 110 | 110 | ||
| @@ -112,14 +112,14 @@ public: | |||
| 112 | 112 | ||
| 113 | bool IsReadable() const override; | 113 | bool IsReadable() const override; |
| 114 | 114 | ||
| 115 | size_t Write(const u8* data, size_t length, size_t offset) override; | 115 | std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override; |
| 116 | 116 | ||
| 117 | bool Rename(std::string_view name) override; | 117 | bool Rename(std::string_view name) override; |
| 118 | 118 | ||
| 119 | private: | 119 | private: |
| 120 | template <bool Subsection, typename BlockType, typename BucketType> | 120 | template <bool Subsection, typename BlockType, typename BucketType> |
| 121 | std::pair<size_t, size_t> SearchBucketEntry(u64 offset, BlockType block, | 121 | std::pair<std::size_t, std::size_t> SearchBucketEntry(u64 offset, BlockType block, |
| 122 | BucketType buckets) const; | 122 | BucketType buckets) const; |
| 123 | 123 | ||
| 124 | RelocationEntry GetRelocationEntry(u64 offset) const; | 124 | RelocationEntry GetRelocationEntry(u64 offset) const; |
| 125 | RelocationEntry GetNextRelocationEntry(u64 offset) const; | 125 | RelocationEntry GetNextRelocationEntry(u64 offset) const; |
diff --git a/src/core/file_sys/partition_filesystem.cpp b/src/core/file_sys/partition_filesystem.cpp index c377edc9c..5791c76ff 100644 --- a/src/core/file_sys/partition_filesystem.cpp +++ b/src/core/file_sys/partition_filesystem.cpp | |||
| @@ -42,21 +42,21 @@ PartitionFilesystem::PartitionFilesystem(std::shared_ptr<VfsFile> file) { | |||
| 42 | 42 | ||
| 43 | is_hfs = pfs_header.magic == Common::MakeMagic('H', 'F', 'S', '0'); | 43 | is_hfs = pfs_header.magic == Common::MakeMagic('H', 'F', 'S', '0'); |
| 44 | 44 | ||
| 45 | size_t entry_size = is_hfs ? sizeof(HFSEntry) : sizeof(PFSEntry); | 45 | std::size_t entry_size = is_hfs ? sizeof(HFSEntry) : sizeof(PFSEntry); |
| 46 | size_t metadata_size = | 46 | std::size_t metadata_size = |
| 47 | sizeof(Header) + (pfs_header.num_entries * entry_size) + pfs_header.strtab_size; | 47 | sizeof(Header) + (pfs_header.num_entries * entry_size) + pfs_header.strtab_size; |
| 48 | 48 | ||
| 49 | // Actually read in now... | 49 | // Actually read in now... |
| 50 | std::vector<u8> file_data = file->ReadBytes(metadata_size); | 50 | std::vector<u8> file_data = file->ReadBytes(metadata_size); |
| 51 | const size_t total_size = file_data.size(); | 51 | const std::size_t total_size = file_data.size(); |
| 52 | 52 | ||
| 53 | if (total_size != metadata_size) { | 53 | if (total_size != metadata_size) { |
| 54 | status = Loader::ResultStatus::ErrorIncorrectPFSFileSize; | 54 | status = Loader::ResultStatus::ErrorIncorrectPFSFileSize; |
| 55 | return; | 55 | return; |
| 56 | } | 56 | } |
| 57 | 57 | ||
| 58 | size_t entries_offset = sizeof(Header); | 58 | std::size_t entries_offset = sizeof(Header); |
| 59 | size_t strtab_offset = entries_offset + (pfs_header.num_entries * entry_size); | 59 | std::size_t strtab_offset = entries_offset + (pfs_header.num_entries * entry_size); |
| 60 | content_offset = strtab_offset + pfs_header.strtab_size; | 60 | content_offset = strtab_offset + pfs_header.strtab_size; |
| 61 | for (u16 i = 0; i < pfs_header.num_entries; i++) { | 61 | for (u16 i = 0; i < pfs_header.num_entries; i++) { |
| 62 | FSEntry entry; | 62 | FSEntry entry; |
| @@ -72,6 +72,8 @@ PartitionFilesystem::PartitionFilesystem(std::shared_ptr<VfsFile> file) { | |||
| 72 | status = Loader::ResultStatus::Success; | 72 | status = Loader::ResultStatus::Success; |
| 73 | } | 73 | } |
| 74 | 74 | ||
| 75 | PartitionFilesystem::~PartitionFilesystem() = default; | ||
| 76 | |||
| 75 | Loader::ResultStatus PartitionFilesystem::GetStatus() const { | 77 | Loader::ResultStatus PartitionFilesystem::GetStatus() const { |
| 76 | return status; | 78 | return status; |
| 77 | } | 79 | } |
diff --git a/src/core/file_sys/partition_filesystem.h b/src/core/file_sys/partition_filesystem.h index be7bc32a8..739c63a7f 100644 --- a/src/core/file_sys/partition_filesystem.h +++ b/src/core/file_sys/partition_filesystem.h | |||
| @@ -25,6 +25,8 @@ namespace FileSys { | |||
| 25 | class PartitionFilesystem : public ReadOnlyVfsDirectory { | 25 | class PartitionFilesystem : public ReadOnlyVfsDirectory { |
| 26 | public: | 26 | public: |
| 27 | explicit PartitionFilesystem(std::shared_ptr<VfsFile> file); | 27 | explicit PartitionFilesystem(std::shared_ptr<VfsFile> file); |
| 28 | ~PartitionFilesystem() override; | ||
| 29 | |||
| 28 | Loader::ResultStatus GetStatus() const; | 30 | Loader::ResultStatus GetStatus() const; |
| 29 | 31 | ||
| 30 | std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; | 32 | std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; |
| @@ -79,7 +81,7 @@ private: | |||
| 79 | 81 | ||
| 80 | Header pfs_header{}; | 82 | Header pfs_header{}; |
| 81 | bool is_hfs = false; | 83 | bool is_hfs = false; |
| 82 | size_t content_offset = 0; | 84 | std::size_t content_offset = 0; |
| 83 | 85 | ||
| 84 | std::vector<VirtualFile> pfs_files; | 86 | std::vector<VirtualFile> pfs_files; |
| 85 | std::vector<VirtualDir> pfs_dirs; | 87 | std::vector<VirtualDir> pfs_dirs; |
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index 6cecab336..4b3b5e665 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | #include "core/file_sys/patch_manager.h" | 11 | #include "core/file_sys/patch_manager.h" |
| 12 | #include "core/file_sys/registered_cache.h" | 12 | #include "core/file_sys/registered_cache.h" |
| 13 | #include "core/file_sys/romfs.h" | 13 | #include "core/file_sys/romfs.h" |
| 14 | #include "core/file_sys/vfs_layered.h" | ||
| 14 | #include "core/hle/service/filesystem/filesystem.h" | 15 | #include "core/hle/service/filesystem/filesystem.h" |
| 15 | #include "core/loader/loader.h" | 16 | #include "core/loader/loader.h" |
| 16 | 17 | ||
| @@ -21,7 +22,7 @@ constexpr u64 SINGLE_BYTE_MODULUS = 0x100; | |||
| 21 | std::string FormatTitleVersion(u32 version, TitleVersionFormat format) { | 22 | std::string FormatTitleVersion(u32 version, TitleVersionFormat format) { |
| 22 | std::array<u8, sizeof(u32)> bytes{}; | 23 | std::array<u8, sizeof(u32)> bytes{}; |
| 23 | bytes[0] = version % SINGLE_BYTE_MODULUS; | 24 | bytes[0] = version % SINGLE_BYTE_MODULUS; |
| 24 | for (size_t i = 1; i < bytes.size(); ++i) { | 25 | for (std::size_t i = 1; i < bytes.size(); ++i) { |
| 25 | version /= SINGLE_BYTE_MODULUS; | 26 | version /= SINGLE_BYTE_MODULUS; |
| 26 | bytes[i] = version % SINGLE_BYTE_MODULUS; | 27 | bytes[i] = version % SINGLE_BYTE_MODULUS; |
| 27 | } | 28 | } |
| @@ -31,16 +32,19 @@ std::string FormatTitleVersion(u32 version, TitleVersionFormat format) { | |||
| 31 | return fmt::format("v{}.{}.{}", bytes[3], bytes[2], bytes[1]); | 32 | return fmt::format("v{}.{}.{}", bytes[3], bytes[2], bytes[1]); |
| 32 | } | 33 | } |
| 33 | 34 | ||
| 34 | constexpr std::array<const char*, 1> PATCH_TYPE_NAMES{ | 35 | constexpr std::array<const char*, 2> PATCH_TYPE_NAMES{ |
| 35 | "Update", | 36 | "Update", |
| 37 | "LayeredFS", | ||
| 36 | }; | 38 | }; |
| 37 | 39 | ||
| 38 | std::string FormatPatchTypeName(PatchType type) { | 40 | std::string FormatPatchTypeName(PatchType type) { |
| 39 | return PATCH_TYPE_NAMES.at(static_cast<size_t>(type)); | 41 | return PATCH_TYPE_NAMES.at(static_cast<std::size_t>(type)); |
| 40 | } | 42 | } |
| 41 | 43 | ||
| 42 | PatchManager::PatchManager(u64 title_id) : title_id(title_id) {} | 44 | PatchManager::PatchManager(u64 title_id) : title_id(title_id) {} |
| 43 | 45 | ||
| 46 | PatchManager::~PatchManager() = default; | ||
| 47 | |||
| 44 | VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { | 48 | VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { |
| 45 | LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id); | 49 | LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id); |
| 46 | 50 | ||
| @@ -64,6 +68,44 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { | |||
| 64 | return exefs; | 68 | return exefs; |
| 65 | } | 69 | } |
| 66 | 70 | ||
| 71 | static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) { | ||
| 72 | const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); | ||
| 73 | if (type != ContentRecordType::Program || load_dir == nullptr || load_dir->GetSize() <= 0) { | ||
| 74 | return; | ||
| 75 | } | ||
| 76 | |||
| 77 | auto extracted = ExtractRomFS(romfs); | ||
| 78 | if (extracted == nullptr) { | ||
| 79 | return; | ||
| 80 | } | ||
| 81 | |||
| 82 | auto patch_dirs = load_dir->GetSubdirectories(); | ||
| 83 | std::sort(patch_dirs.begin(), patch_dirs.end(), | ||
| 84 | [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); | ||
| 85 | |||
| 86 | std::vector<VirtualDir> layers; | ||
| 87 | layers.reserve(patch_dirs.size() + 1); | ||
| 88 | for (const auto& subdir : patch_dirs) { | ||
| 89 | auto romfs_dir = subdir->GetSubdirectory("romfs"); | ||
| 90 | if (romfs_dir != nullptr) | ||
| 91 | layers.push_back(std::move(romfs_dir)); | ||
| 92 | } | ||
| 93 | layers.push_back(std::move(extracted)); | ||
| 94 | |||
| 95 | auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers)); | ||
| 96 | if (layered == nullptr) { | ||
| 97 | return; | ||
| 98 | } | ||
| 99 | |||
| 100 | auto packed = CreateRomFS(std::move(layered)); | ||
| 101 | if (packed == nullptr) { | ||
| 102 | return; | ||
| 103 | } | ||
| 104 | |||
| 105 | LOG_INFO(Loader, " RomFS: LayeredFS patches applied successfully"); | ||
| 106 | romfs = std::move(packed); | ||
| 107 | } | ||
| 108 | |||
| 67 | VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, | 109 | VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, |
| 68 | ContentRecordType type) const { | 110 | ContentRecordType type) const { |
| 69 | LOG_INFO(Loader, "Patching RomFS for title_id={:016X}, type={:02X}", title_id, | 111 | LOG_INFO(Loader, "Patching RomFS for title_id={:016X}, type={:02X}", title_id, |
| @@ -87,6 +129,9 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, | |||
| 87 | } | 129 | } |
| 88 | } | 130 | } |
| 89 | 131 | ||
| 132 | // LayeredFS | ||
| 133 | ApplyLayeredFS(romfs, title_id, type); | ||
| 134 | |||
| 90 | return romfs; | 135 | return romfs; |
| 91 | } | 136 | } |
| 92 | 137 | ||
| @@ -112,6 +157,10 @@ std::map<PatchType, std::string> PatchManager::GetPatchVersionNames() const { | |||
| 112 | } | 157 | } |
| 113 | } | 158 | } |
| 114 | 159 | ||
| 160 | const auto lfs_dir = Service::FileSystem::GetModificationLoadRoot(title_id); | ||
| 161 | if (lfs_dir != nullptr && lfs_dir->GetSize() > 0) | ||
| 162 | out.insert_or_assign(PatchType::LayeredFS, ""); | ||
| 163 | |||
| 115 | return out; | 164 | return out; |
| 116 | } | 165 | } |
| 117 | 166 | ||
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h index b521977b2..464f17515 100644 --- a/src/core/file_sys/patch_manager.h +++ b/src/core/file_sys/patch_manager.h | |||
| @@ -26,6 +26,7 @@ std::string FormatTitleVersion(u32 version, | |||
| 26 | 26 | ||
| 27 | enum class PatchType { | 27 | enum class PatchType { |
| 28 | Update, | 28 | Update, |
| 29 | LayeredFS, | ||
| 29 | }; | 30 | }; |
| 30 | 31 | ||
| 31 | std::string FormatPatchTypeName(PatchType type); | 32 | std::string FormatPatchTypeName(PatchType type); |
| @@ -34,6 +35,7 @@ std::string FormatPatchTypeName(PatchType type); | |||
| 34 | class PatchManager { | 35 | class PatchManager { |
| 35 | public: | 36 | public: |
| 36 | explicit PatchManager(u64 title_id); | 37 | explicit PatchManager(u64 title_id); |
| 38 | ~PatchManager(); | ||
| 37 | 39 | ||
| 38 | // Currently tracked ExeFS patches: | 40 | // Currently tracked ExeFS patches: |
| 39 | // - Game Updates | 41 | // - Game Updates |
| @@ -41,6 +43,7 @@ public: | |||
| 41 | 43 | ||
| 42 | // Currently tracked RomFS patches: | 44 | // Currently tracked RomFS patches: |
| 43 | // - Game Updates | 45 | // - Game Updates |
| 46 | // - LayeredFS | ||
| 44 | VirtualFile PatchRomFS(VirtualFile base, u64 ivfc_offset, | 47 | VirtualFile PatchRomFS(VirtualFile base, u64 ivfc_offset, |
| 45 | ContentRecordType type = ContentRecordType::Program) const; | 48 | ContentRecordType type = ContentRecordType::Program) const; |
| 46 | 49 | ||
diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp index ccb685526..8903ed1d3 100644 --- a/src/core/file_sys/program_metadata.cpp +++ b/src/core/file_sys/program_metadata.cpp | |||
| @@ -12,8 +12,12 @@ | |||
| 12 | 12 | ||
| 13 | namespace FileSys { | 13 | namespace FileSys { |
| 14 | 14 | ||
| 15 | ProgramMetadata::ProgramMetadata() = default; | ||
| 16 | |||
| 17 | ProgramMetadata::~ProgramMetadata() = default; | ||
| 18 | |||
| 15 | Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) { | 19 | Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) { |
| 16 | size_t total_size = static_cast<size_t>(file->GetSize()); | 20 | std::size_t total_size = static_cast<std::size_t>(file->GetSize()); |
| 17 | if (total_size < sizeof(Header)) | 21 | if (total_size < sizeof(Header)) |
| 18 | return Loader::ResultStatus::ErrorBadNPDMHeader; | 22 | return Loader::ResultStatus::ErrorBadNPDMHeader; |
| 19 | 23 | ||
| @@ -79,10 +83,12 @@ void ProgramMetadata::Print() const { | |||
| 79 | 83 | ||
| 80 | auto address_space = "Unknown"; | 84 | auto address_space = "Unknown"; |
| 81 | switch (npdm_header.address_space_type) { | 85 | switch (npdm_header.address_space_type) { |
| 82 | case ProgramAddressSpaceType::Is64Bit: | 86 | case ProgramAddressSpaceType::Is36Bit: |
| 87 | case ProgramAddressSpaceType::Is39Bit: | ||
| 83 | address_space = "64-bit"; | 88 | address_space = "64-bit"; |
| 84 | break; | 89 | break; |
| 85 | case ProgramAddressSpaceType::Is32Bit: | 90 | case ProgramAddressSpaceType::Is32Bit: |
| 91 | case ProgramAddressSpaceType::Is32BitNoMap: | ||
| 86 | address_space = "32-bit"; | 92 | address_space = "32-bit"; |
| 87 | break; | 93 | break; |
| 88 | } | 94 | } |
diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h index 3c0a49f16..e4470d6f0 100644 --- a/src/core/file_sys/program_metadata.h +++ b/src/core/file_sys/program_metadata.h | |||
| @@ -17,8 +17,10 @@ enum class ResultStatus : u16; | |||
| 17 | namespace FileSys { | 17 | namespace FileSys { |
| 18 | 18 | ||
| 19 | enum class ProgramAddressSpaceType : u8 { | 19 | enum class ProgramAddressSpaceType : u8 { |
| 20 | Is64Bit = 1, | 20 | Is32Bit = 0, |
| 21 | Is32Bit = 2, | 21 | Is36Bit = 1, |
| 22 | Is32BitNoMap = 2, | ||
| 23 | Is39Bit = 3, | ||
| 22 | }; | 24 | }; |
| 23 | 25 | ||
| 24 | enum class ProgramFilePermission : u64 { | 26 | enum class ProgramFilePermission : u64 { |
| @@ -36,6 +38,9 @@ enum class ProgramFilePermission : u64 { | |||
| 36 | */ | 38 | */ |
| 37 | class ProgramMetadata { | 39 | class ProgramMetadata { |
| 38 | public: | 40 | public: |
| 41 | ProgramMetadata(); | ||
| 42 | ~ProgramMetadata(); | ||
| 43 | |||
| 39 | Loader::ResultStatus Load(VirtualFile file); | 44 | Loader::ResultStatus Load(VirtualFile file); |
| 40 | 45 | ||
| 41 | bool Is64BitProgram() const; | 46 | bool Is64BitProgram() const; |
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index 7361a67be..e9b040689 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp | |||
| @@ -18,6 +18,10 @@ | |||
| 18 | #include "core/loader/loader.h" | 18 | #include "core/loader/loader.h" |
| 19 | 19 | ||
| 20 | namespace FileSys { | 20 | namespace FileSys { |
| 21 | |||
| 22 | // The size of blocks to use when vfs raw copying into nand. | ||
| 23 | constexpr size_t VFS_RC_LARGE_COPY_BLOCK = 0x400000; | ||
| 24 | |||
| 21 | std::string RegisteredCacheEntry::DebugInfo() const { | 25 | std::string RegisteredCacheEntry::DebugInfo() const { |
| 22 | return fmt::format("title_id={:016X}, content_type={:02X}", title_id, static_cast<u8>(type)); | 26 | return fmt::format("title_id={:016X}, content_type={:02X}", title_id, static_cast<u8>(type)); |
| 23 | } | 27 | } |
| @@ -62,11 +66,11 @@ static std::string GetCNMTName(TitleType type, u64 title_id) { | |||
| 62 | "" ///< Currently unknown 'DeltaTitle' | 66 | "" ///< Currently unknown 'DeltaTitle' |
| 63 | }; | 67 | }; |
| 64 | 68 | ||
| 65 | auto index = static_cast<size_t>(type); | 69 | auto index = static_cast<std::size_t>(type); |
| 66 | // If the index is after the jump in TitleType, subtract it out. | 70 | // If the index is after the jump in TitleType, subtract it out. |
| 67 | if (index >= static_cast<size_t>(TitleType::Application)) { | 71 | if (index >= static_cast<std::size_t>(TitleType::Application)) { |
| 68 | index -= static_cast<size_t>(TitleType::Application) - | 72 | index -= static_cast<std::size_t>(TitleType::Application) - |
| 69 | static_cast<size_t>(TitleType::FirmwarePackageB); | 73 | static_cast<std::size_t>(TitleType::FirmwarePackageB); |
| 70 | } | 74 | } |
| 71 | return fmt::format("{}_{:016x}.cnmt", TITLE_TYPE_NAMES[index], title_id); | 75 | return fmt::format("{}_{:016x}.cnmt", TITLE_TYPE_NAMES[index], title_id); |
| 72 | } | 76 | } |
| @@ -105,7 +109,7 @@ VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir, | |||
| 105 | } else { | 109 | } else { |
| 106 | std::vector<VirtualFile> concat; | 110 | std::vector<VirtualFile> concat; |
| 107 | // Since the files are a two-digit hex number, max is FF. | 111 | // Since the files are a two-digit hex number, max is FF. |
| 108 | for (size_t i = 0; i < 0x100; ++i) { | 112 | for (std::size_t i = 0; i < 0x100; ++i) { |
| 109 | auto next = nca_dir->GetFile(fmt::format("{:02X}", i)); | 113 | auto next = nca_dir->GetFile(fmt::format("{:02X}", i)); |
| 110 | if (next != nullptr) { | 114 | if (next != nullptr) { |
| 111 | concat.push_back(std::move(next)); | 115 | concat.push_back(std::move(next)); |
| @@ -121,7 +125,7 @@ VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir, | |||
| 121 | if (concat.empty()) | 125 | if (concat.empty()) |
| 122 | return nullptr; | 126 | return nullptr; |
| 123 | 127 | ||
| 124 | file = FileSys::ConcatenateFiles(concat); | 128 | file = ConcatenatedVfsFile::MakeConcatenatedFile(concat, concat.front()->GetName()); |
| 125 | } | 129 | } |
| 126 | 130 | ||
| 127 | return file; | 131 | return file; |
| @@ -480,7 +484,8 @@ InstallResult RegisteredCache::RawInstallNCA(std::shared_ptr<NCA> nca, const Vfs | |||
| 480 | auto out = dir->CreateFileRelative(path); | 484 | auto out = dir->CreateFileRelative(path); |
| 481 | if (out == nullptr) | 485 | if (out == nullptr) |
| 482 | return InstallResult::ErrorCopyFailed; | 486 | return InstallResult::ErrorCopyFailed; |
| 483 | return copy(in, out) ? InstallResult::Success : InstallResult::ErrorCopyFailed; | 487 | return copy(in, out, VFS_RC_LARGE_COPY_BLOCK) ? InstallResult::Success |
| 488 | : InstallResult::ErrorCopyFailed; | ||
| 484 | } | 489 | } |
| 485 | 490 | ||
| 486 | bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) { | 491 | bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) { |
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h index f487b0cf0..c0cd59fc5 100644 --- a/src/core/file_sys/registered_cache.h +++ b/src/core/file_sys/registered_cache.h | |||
| @@ -27,7 +27,7 @@ struct ContentRecord; | |||
| 27 | 27 | ||
| 28 | using NcaID = std::array<u8, 0x10>; | 28 | using NcaID = std::array<u8, 0x10>; |
| 29 | using RegisteredCacheParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>; | 29 | using RegisteredCacheParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>; |
| 30 | using VfsCopyFunction = std::function<bool(VirtualFile, VirtualFile)>; | 30 | using VfsCopyFunction = std::function<bool(const VirtualFile&, const VirtualFile&, size_t)>; |
| 31 | 31 | ||
| 32 | enum class InstallResult { | 32 | enum class InstallResult { |
| 33 | Success, | 33 | Success, |
diff --git a/src/core/file_sys/romfs.cpp b/src/core/file_sys/romfs.cpp index e490c8ace..5910f7046 100644 --- a/src/core/file_sys/romfs.cpp +++ b/src/core/file_sys/romfs.cpp | |||
| @@ -4,8 +4,10 @@ | |||
| 4 | 4 | ||
| 5 | #include "common/common_types.h" | 5 | #include "common/common_types.h" |
| 6 | #include "common/swap.h" | 6 | #include "common/swap.h" |
| 7 | #include "core/file_sys/fsmitm_romfsbuild.h" | ||
| 7 | #include "core/file_sys/romfs.h" | 8 | #include "core/file_sys/romfs.h" |
| 8 | #include "core/file_sys/vfs.h" | 9 | #include "core/file_sys/vfs.h" |
| 10 | #include "core/file_sys/vfs_concat.h" | ||
| 9 | #include "core/file_sys/vfs_offset.h" | 11 | #include "core/file_sys/vfs_offset.h" |
| 10 | #include "core/file_sys/vfs_vector.h" | 12 | #include "core/file_sys/vfs_vector.h" |
| 11 | 13 | ||
| @@ -49,7 +51,7 @@ struct FileEntry { | |||
| 49 | static_assert(sizeof(FileEntry) == 0x20, "FileEntry has incorrect size."); | 51 | static_assert(sizeof(FileEntry) == 0x20, "FileEntry has incorrect size."); |
| 50 | 52 | ||
| 51 | template <typename Entry> | 53 | template <typename Entry> |
| 52 | static std::pair<Entry, std::string> GetEntry(const VirtualFile& file, size_t offset) { | 54 | static std::pair<Entry, std::string> GetEntry(const VirtualFile& file, std::size_t offset) { |
| 53 | Entry entry{}; | 55 | Entry entry{}; |
| 54 | if (file->ReadObject(&entry, offset) != sizeof(Entry)) | 56 | if (file->ReadObject(&entry, offset) != sizeof(Entry)) |
| 55 | return {}; | 57 | return {}; |
| @@ -59,8 +61,8 @@ static std::pair<Entry, std::string> GetEntry(const VirtualFile& file, size_t of | |||
| 59 | return {entry, string}; | 61 | return {entry, string}; |
| 60 | } | 62 | } |
| 61 | 63 | ||
| 62 | void ProcessFile(VirtualFile file, size_t file_offset, size_t data_offset, u32 this_file_offset, | 64 | void ProcessFile(VirtualFile file, std::size_t file_offset, std::size_t data_offset, |
| 63 | std::shared_ptr<VectorVfsDirectory> parent) { | 65 | u32 this_file_offset, std::shared_ptr<VectorVfsDirectory> parent) { |
| 64 | while (true) { | 66 | while (true) { |
| 65 | auto entry = GetEntry<FileEntry>(file, file_offset + this_file_offset); | 67 | auto entry = GetEntry<FileEntry>(file, file_offset + this_file_offset); |
| 66 | 68 | ||
| @@ -74,8 +76,9 @@ void ProcessFile(VirtualFile file, size_t file_offset, size_t data_offset, u32 t | |||
| 74 | } | 76 | } |
| 75 | } | 77 | } |
| 76 | 78 | ||
| 77 | void ProcessDirectory(VirtualFile file, size_t dir_offset, size_t file_offset, size_t data_offset, | 79 | void ProcessDirectory(VirtualFile file, std::size_t dir_offset, std::size_t file_offset, |
| 78 | u32 this_dir_offset, std::shared_ptr<VectorVfsDirectory> parent) { | 80 | std::size_t data_offset, u32 this_dir_offset, |
| 81 | std::shared_ptr<VectorVfsDirectory> parent) { | ||
| 79 | while (true) { | 82 | while (true) { |
| 80 | auto entry = GetEntry<DirectoryEntry>(file, dir_offset + this_dir_offset); | 83 | auto entry = GetEntry<DirectoryEntry>(file, dir_offset + this_dir_offset); |
| 81 | auto current = std::make_shared<VectorVfsDirectory>( | 84 | auto current = std::make_shared<VectorVfsDirectory>( |
| @@ -97,7 +100,7 @@ void ProcessDirectory(VirtualFile file, size_t dir_offset, size_t file_offset, s | |||
| 97 | } | 100 | } |
| 98 | } | 101 | } |
| 99 | 102 | ||
| 100 | VirtualDir ExtractRomFS(VirtualFile file) { | 103 | VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) { |
| 101 | RomFSHeader header{}; | 104 | RomFSHeader header{}; |
| 102 | if (file->ReadObject(&header) != sizeof(RomFSHeader)) | 105 | if (file->ReadObject(&header) != sizeof(RomFSHeader)) |
| 103 | return nullptr; | 106 | return nullptr; |
| @@ -116,9 +119,22 @@ VirtualDir ExtractRomFS(VirtualFile file) { | |||
| 116 | 119 | ||
| 117 | VirtualDir out = std::move(root); | 120 | VirtualDir out = std::move(root); |
| 118 | 121 | ||
| 119 | while (out->GetSubdirectory("") != nullptr) | 122 | while (out->GetSubdirectories().size() == 1 && out->GetFiles().empty()) { |
| 120 | out = out->GetSubdirectory(""); | 123 | if (out->GetSubdirectories().front()->GetName() == "data" && |
| 124 | type == RomFSExtractionType::Truncated) | ||
| 125 | break; | ||
| 126 | out = out->GetSubdirectories().front(); | ||
| 127 | } | ||
| 121 | 128 | ||
| 122 | return out; | 129 | return out; |
| 123 | } | 130 | } |
| 131 | |||
| 132 | VirtualFile CreateRomFS(VirtualDir dir) { | ||
| 133 | if (dir == nullptr) | ||
| 134 | return nullptr; | ||
| 135 | |||
| 136 | RomFSBuildContext ctx{dir}; | ||
| 137 | return ConcatenatedVfsFile::MakeConcatenatedFile(0, ctx.Build(), dir->GetName()); | ||
| 138 | } | ||
| 139 | |||
| 124 | } // namespace FileSys | 140 | } // namespace FileSys |
diff --git a/src/core/file_sys/romfs.h b/src/core/file_sys/romfs.h index e54a7d7a9..ecd1eb725 100644 --- a/src/core/file_sys/romfs.h +++ b/src/core/file_sys/romfs.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <map> | ||
| 8 | #include "common/common_funcs.h" | 9 | #include "common/common_funcs.h" |
| 9 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 10 | #include "common/swap.h" | 11 | #include "common/swap.h" |
| @@ -12,6 +13,8 @@ | |||
| 12 | 13 | ||
| 13 | namespace FileSys { | 14 | namespace FileSys { |
| 14 | 15 | ||
| 16 | struct RomFSHeader; | ||
| 17 | |||
| 15 | struct IVFCLevel { | 18 | struct IVFCLevel { |
| 16 | u64_le offset; | 19 | u64_le offset; |
| 17 | u64_le size; | 20 | u64_le size; |
| @@ -29,8 +32,18 @@ struct IVFCHeader { | |||
| 29 | }; | 32 | }; |
| 30 | static_assert(sizeof(IVFCHeader) == 0xE0, "IVFCHeader has incorrect size."); | 33 | static_assert(sizeof(IVFCHeader) == 0xE0, "IVFCHeader has incorrect size."); |
| 31 | 34 | ||
| 35 | enum class RomFSExtractionType { | ||
| 36 | Full, // Includes data directory | ||
| 37 | Truncated, // Traverses into data directory | ||
| 38 | }; | ||
| 39 | |||
| 32 | // Converts a RomFS binary blob to VFS Filesystem | 40 | // Converts a RomFS binary blob to VFS Filesystem |
| 33 | // Returns nullptr on failure | 41 | // Returns nullptr on failure |
| 34 | VirtualDir ExtractRomFS(VirtualFile file); | 42 | VirtualDir ExtractRomFS(VirtualFile file, |
| 43 | RomFSExtractionType type = RomFSExtractionType::Truncated); | ||
| 44 | |||
| 45 | // Converts a VFS filesystem into a RomFS binary | ||
| 46 | // Returns nullptr on failure | ||
| 47 | VirtualFile CreateRomFS(VirtualDir dir); | ||
| 35 | 48 | ||
| 36 | } // namespace FileSys | 49 | } // namespace FileSys |
diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp index d9d90939e..d027a8d59 100644 --- a/src/core/file_sys/romfs_factory.cpp +++ b/src/core/file_sys/romfs_factory.cpp | |||
| @@ -28,11 +28,13 @@ RomFSFactory::RomFSFactory(Loader::AppLoader& app_loader) { | |||
| 28 | ivfc_offset = app_loader.ReadRomFSIVFCOffset(); | 28 | ivfc_offset = app_loader.ReadRomFSIVFCOffset(); |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | RomFSFactory::~RomFSFactory() = default; | ||
| 32 | |||
| 31 | ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() { | 33 | ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() { |
| 32 | if (!updatable) | 34 | if (!updatable) |
| 33 | return MakeResult<VirtualFile>(file); | 35 | return MakeResult<VirtualFile>(file); |
| 34 | 36 | ||
| 35 | const PatchManager patch_manager(Core::CurrentProcess()->program_id); | 37 | const PatchManager patch_manager(Core::CurrentProcess()->GetTitleID()); |
| 36 | return MakeResult<VirtualFile>(patch_manager.PatchRomFS(file, ivfc_offset)); | 38 | return MakeResult<VirtualFile>(patch_manager.PatchRomFS(file, ivfc_offset)); |
| 37 | } | 39 | } |
| 38 | 40 | ||
diff --git a/src/core/file_sys/romfs_factory.h b/src/core/file_sys/romfs_factory.h index 26b8f46cc..2cace8180 100644 --- a/src/core/file_sys/romfs_factory.h +++ b/src/core/file_sys/romfs_factory.h | |||
| @@ -30,6 +30,7 @@ enum class StorageId : u8 { | |||
| 30 | class RomFSFactory { | 30 | class RomFSFactory { |
| 31 | public: | 31 | public: |
| 32 | explicit RomFSFactory(Loader::AppLoader& app_loader); | 32 | explicit RomFSFactory(Loader::AppLoader& app_loader); |
| 33 | ~RomFSFactory(); | ||
| 33 | 34 | ||
| 34 | ResultVal<VirtualFile> OpenCurrentProcess(); | 35 | ResultVal<VirtualFile> OpenCurrentProcess(); |
| 35 | ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type); | 36 | ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type); |
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp index e437d34e5..47f2ab9e0 100644 --- a/src/core/file_sys/savedata_factory.cpp +++ b/src/core/file_sys/savedata_factory.cpp | |||
| @@ -20,6 +20,8 @@ std::string SaveDataDescriptor::DebugInfo() const { | |||
| 20 | 20 | ||
| 21 | SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {} | 21 | SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {} |
| 22 | 22 | ||
| 23 | SaveDataFactory::~SaveDataFactory() = default; | ||
| 24 | |||
| 23 | ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescriptor meta) { | 25 | ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescriptor meta) { |
| 24 | if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) { | 26 | if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) { |
| 25 | if (meta.zero_1 != 0) { | 27 | if (meta.zero_1 != 0) { |
| @@ -79,16 +81,16 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ | |||
| 79 | // According to switchbrew, if a save is of type SaveData and the title id field is 0, it should | 81 | // According to switchbrew, if a save is of type SaveData and the title id field is 0, it should |
| 80 | // be interpreted as the title id of the current process. | 82 | // be interpreted as the title id of the current process. |
| 81 | if (type == SaveDataType::SaveData && title_id == 0) | 83 | if (type == SaveDataType::SaveData && title_id == 0) |
| 82 | title_id = Core::CurrentProcess()->program_id; | 84 | title_id = Core::CurrentProcess()->GetTitleID(); |
| 83 | 85 | ||
| 84 | std::string out; | 86 | std::string out; |
| 85 | 87 | ||
| 86 | switch (space) { | 88 | switch (space) { |
| 87 | case SaveDataSpaceId::NandSystem: | 89 | case SaveDataSpaceId::NandSystem: |
| 88 | out = "/system/save/"; | 90 | out = "/system/"; |
| 89 | break; | 91 | break; |
| 90 | case SaveDataSpaceId::NandUser: | 92 | case SaveDataSpaceId::NandUser: |
| 91 | out = "/user/save/"; | 93 | out = "/user/"; |
| 92 | break; | 94 | break; |
| 93 | default: | 95 | default: |
| 94 | ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space)); | 96 | ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space)); |
| @@ -96,9 +98,12 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ | |||
| 96 | 98 | ||
| 97 | switch (type) { | 99 | switch (type) { |
| 98 | case SaveDataType::SystemSaveData: | 100 | case SaveDataType::SystemSaveData: |
| 99 | return fmt::format("{}{:016X}/{:016X}{:016X}", out, save_id, user_id[1], user_id[0]); | 101 | return fmt::format("{}save/{:016X}/{:016X}{:016X}", out, save_id, user_id[1], user_id[0]); |
| 100 | case SaveDataType::SaveData: | 102 | case SaveDataType::SaveData: |
| 101 | return fmt::format("{}{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0], | 103 | return fmt::format("{}save/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0], |
| 104 | title_id); | ||
| 105 | case SaveDataType::TemporaryStorage: | ||
| 106 | return fmt::format("{}temp/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0], | ||
| 102 | title_id); | 107 | title_id); |
| 103 | default: | 108 | default: |
| 104 | ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type)); | 109 | ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type)); |
diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h index ba978695b..d69ef6741 100644 --- a/src/core/file_sys/savedata_factory.h +++ b/src/core/file_sys/savedata_factory.h | |||
| @@ -48,6 +48,7 @@ static_assert(sizeof(SaveDataDescriptor) == 0x40, "SaveDataDescriptor has incorr | |||
| 48 | class SaveDataFactory { | 48 | class SaveDataFactory { |
| 49 | public: | 49 | public: |
| 50 | explicit SaveDataFactory(VirtualDir dir); | 50 | explicit SaveDataFactory(VirtualDir dir); |
| 51 | ~SaveDataFactory(); | ||
| 51 | 52 | ||
| 52 | ResultVal<VirtualDir> Open(SaveDataSpaceId space, SaveDataDescriptor meta); | 53 | ResultVal<VirtualDir> Open(SaveDataSpaceId space, SaveDataDescriptor meta); |
| 53 | 54 | ||
diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h index 1120a4920..e85a2b76e 100644 --- a/src/core/file_sys/submission_package.h +++ b/src/core/file_sys/submission_package.h | |||
| @@ -24,7 +24,7 @@ enum class ContentRecordType : u8; | |||
| 24 | class NSP : public ReadOnlyVfsDirectory { | 24 | class NSP : public ReadOnlyVfsDirectory { |
| 25 | public: | 25 | public: |
| 26 | explicit NSP(VirtualFile file); | 26 | explicit NSP(VirtualFile file); |
| 27 | ~NSP(); | 27 | ~NSP() override; |
| 28 | 28 | ||
| 29 | Loader::ResultStatus GetStatus() const; | 29 | Loader::ResultStatus GetStatus() const; |
| 30 | Loader::ResultStatus GetProgramStatus(u64 title_id) const; | 30 | Loader::ResultStatus GetProgramStatus(u64 title_id) const; |
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp index 146c839f4..bfe50da73 100644 --- a/src/core/file_sys/vfs.cpp +++ b/src/core/file_sys/vfs.cpp | |||
| @@ -167,18 +167,18 @@ std::string VfsFile::GetExtension() const { | |||
| 167 | 167 | ||
| 168 | VfsDirectory::~VfsDirectory() = default; | 168 | VfsDirectory::~VfsDirectory() = default; |
| 169 | 169 | ||
| 170 | boost::optional<u8> VfsFile::ReadByte(size_t offset) const { | 170 | boost::optional<u8> VfsFile::ReadByte(std::size_t offset) const { |
| 171 | u8 out{}; | 171 | u8 out{}; |
| 172 | size_t size = Read(&out, 1, offset); | 172 | std::size_t size = Read(&out, 1, offset); |
| 173 | if (size == 1) | 173 | if (size == 1) |
| 174 | return out; | 174 | return out; |
| 175 | 175 | ||
| 176 | return boost::none; | 176 | return boost::none; |
| 177 | } | 177 | } |
| 178 | 178 | ||
| 179 | std::vector<u8> VfsFile::ReadBytes(size_t size, size_t offset) const { | 179 | std::vector<u8> VfsFile::ReadBytes(std::size_t size, std::size_t offset) const { |
| 180 | std::vector<u8> out(size); | 180 | std::vector<u8> out(size); |
| 181 | size_t read_size = Read(out.data(), size, offset); | 181 | std::size_t read_size = Read(out.data(), size, offset); |
| 182 | out.resize(read_size); | 182 | out.resize(read_size); |
| 183 | return out; | 183 | return out; |
| 184 | } | 184 | } |
| @@ -187,11 +187,11 @@ std::vector<u8> VfsFile::ReadAllBytes() const { | |||
| 187 | return ReadBytes(GetSize()); | 187 | return ReadBytes(GetSize()); |
| 188 | } | 188 | } |
| 189 | 189 | ||
| 190 | bool VfsFile::WriteByte(u8 data, size_t offset) { | 190 | bool VfsFile::WriteByte(u8 data, std::size_t offset) { |
| 191 | return Write(&data, 1, offset) == 1; | 191 | return Write(&data, 1, offset) == 1; |
| 192 | } | 192 | } |
| 193 | 193 | ||
| 194 | size_t VfsFile::WriteBytes(const std::vector<u8>& data, size_t offset) { | 194 | std::size_t VfsFile::WriteBytes(const std::vector<u8>& data, std::size_t offset) { |
| 195 | return Write(data.data(), data.size(), offset); | 195 | return Write(data.data(), data.size(), offset); |
| 196 | } | 196 | } |
| 197 | 197 | ||
| @@ -215,7 +215,7 @@ std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(std::string_view path) co | |||
| 215 | } | 215 | } |
| 216 | 216 | ||
| 217 | auto dir = GetSubdirectory(vec[0]); | 217 | auto dir = GetSubdirectory(vec[0]); |
| 218 | for (size_t component = 1; component < vec.size() - 1; ++component) { | 218 | for (std::size_t component = 1; component < vec.size() - 1; ++component) { |
| 219 | if (dir == nullptr) { | 219 | if (dir == nullptr) { |
| 220 | return nullptr; | 220 | return nullptr; |
| 221 | } | 221 | } |
| @@ -249,7 +249,7 @@ std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryRelative(std::string_vie | |||
| 249 | } | 249 | } |
| 250 | 250 | ||
| 251 | auto dir = GetSubdirectory(vec[0]); | 251 | auto dir = GetSubdirectory(vec[0]); |
| 252 | for (size_t component = 1; component < vec.size(); ++component) { | 252 | for (std::size_t component = 1; component < vec.size(); ++component) { |
| 253 | if (dir == nullptr) { | 253 | if (dir == nullptr) { |
| 254 | return nullptr; | 254 | return nullptr; |
| 255 | } | 255 | } |
| @@ -286,7 +286,7 @@ bool VfsDirectory::IsRoot() const { | |||
| 286 | return GetParentDirectory() == nullptr; | 286 | return GetParentDirectory() == nullptr; |
| 287 | } | 287 | } |
| 288 | 288 | ||
| 289 | size_t VfsDirectory::GetSize() const { | 289 | std::size_t VfsDirectory::GetSize() const { |
| 290 | const auto& files = GetFiles(); | 290 | const auto& files = GetFiles(); |
| 291 | const auto sum_sizes = [](const auto& range) { | 291 | const auto sum_sizes = [](const auto& range) { |
| 292 | return std::accumulate(range.begin(), range.end(), 0ULL, | 292 | return std::accumulate(range.begin(), range.end(), 0ULL, |
| @@ -399,6 +399,15 @@ bool VfsDirectory::Copy(std::string_view src, std::string_view dest) { | |||
| 399 | return f2->WriteBytes(f1->ReadAllBytes()) == f1->GetSize(); | 399 | return f2->WriteBytes(f1->ReadAllBytes()) == f1->GetSize(); |
| 400 | } | 400 | } |
| 401 | 401 | ||
| 402 | std::map<std::string, VfsEntryType, std::less<>> VfsDirectory::GetEntries() const { | ||
| 403 | std::map<std::string, VfsEntryType, std::less<>> out; | ||
| 404 | for (const auto& dir : GetSubdirectories()) | ||
| 405 | out.emplace(dir->GetName(), VfsEntryType::Directory); | ||
| 406 | for (const auto& file : GetFiles()) | ||
| 407 | out.emplace(file->GetName(), VfsEntryType::File); | ||
| 408 | return out; | ||
| 409 | } | ||
| 410 | |||
| 402 | std::string VfsDirectory::GetFullPath() const { | 411 | std::string VfsDirectory::GetFullPath() const { |
| 403 | if (IsRoot()) | 412 | if (IsRoot()) |
| 404 | return GetName(); | 413 | return GetName(); |
| @@ -434,13 +443,13 @@ bool ReadOnlyVfsDirectory::Rename(std::string_view name) { | |||
| 434 | return false; | 443 | return false; |
| 435 | } | 444 | } |
| 436 | 445 | ||
| 437 | bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, size_t block_size) { | 446 | bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, std::size_t block_size) { |
| 438 | if (file1->GetSize() != file2->GetSize()) | 447 | if (file1->GetSize() != file2->GetSize()) |
| 439 | return false; | 448 | return false; |
| 440 | 449 | ||
| 441 | std::vector<u8> f1_v(block_size); | 450 | std::vector<u8> f1_v(block_size); |
| 442 | std::vector<u8> f2_v(block_size); | 451 | std::vector<u8> f2_v(block_size); |
| 443 | for (size_t i = 0; i < file1->GetSize(); i += block_size) { | 452 | for (std::size_t i = 0; i < file1->GetSize(); i += block_size) { |
| 444 | auto f1_vs = file1->Read(f1_v.data(), block_size, i); | 453 | auto f1_vs = file1->Read(f1_v.data(), block_size, i); |
| 445 | auto f2_vs = file2->Read(f2_v.data(), block_size, i); | 454 | auto f2_vs = file2->Read(f2_v.data(), block_size, i); |
| 446 | 455 | ||
| @@ -454,13 +463,41 @@ bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, size_t block | |||
| 454 | return true; | 463 | return true; |
| 455 | } | 464 | } |
| 456 | 465 | ||
| 457 | bool VfsRawCopy(VirtualFile src, VirtualFile dest) { | 466 | bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, std::size_t block_size) { |
| 458 | if (src == nullptr || dest == nullptr) | 467 | if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable()) |
| 459 | return false; | 468 | return false; |
| 460 | if (!dest->Resize(src->GetSize())) | 469 | if (!dest->Resize(src->GetSize())) |
| 461 | return false; | 470 | return false; |
| 462 | std::vector<u8> data = src->ReadAllBytes(); | 471 | |
| 463 | return dest->WriteBytes(data, 0) == data.size(); | 472 | std::vector<u8> temp(std::min(block_size, src->GetSize())); |
| 473 | for (std::size_t i = 0; i < src->GetSize(); i += block_size) { | ||
| 474 | const auto read = std::min(block_size, src->GetSize() - i); | ||
| 475 | const auto block = src->Read(temp.data(), read, i); | ||
| 476 | |||
| 477 | if (dest->Write(temp.data(), read, i) != read) | ||
| 478 | return false; | ||
| 479 | } | ||
| 480 | |||
| 481 | return true; | ||
| 482 | } | ||
| 483 | |||
| 484 | bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, std::size_t block_size) { | ||
| 485 | if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable()) | ||
| 486 | return false; | ||
| 487 | |||
| 488 | for (const auto& file : src->GetFiles()) { | ||
| 489 | const auto out = dest->CreateFile(file->GetName()); | ||
| 490 | if (!VfsRawCopy(file, out, block_size)) | ||
| 491 | return false; | ||
| 492 | } | ||
| 493 | |||
| 494 | for (const auto& dir : src->GetSubdirectories()) { | ||
| 495 | const auto out = dest->CreateSubdirectory(dir->GetName()); | ||
| 496 | if (!VfsRawCopyD(dir, out, block_size)) | ||
| 497 | return false; | ||
| 498 | } | ||
| 499 | |||
| 500 | return true; | ||
| 464 | } | 501 | } |
| 465 | 502 | ||
| 466 | VirtualDir GetOrCreateDirectoryRelative(const VirtualDir& rel, std::string_view path) { | 503 | VirtualDir GetOrCreateDirectoryRelative(const VirtualDir& rel, std::string_view path) { |
diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h index 5142a3e86..270291631 100644 --- a/src/core/file_sys/vfs.h +++ b/src/core/file_sys/vfs.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <map> | ||
| 7 | #include <memory> | 8 | #include <memory> |
| 8 | #include <string> | 9 | #include <string> |
| 9 | #include <string_view> | 10 | #include <string_view> |
| @@ -92,9 +93,9 @@ public: | |||
| 92 | // Retrieves the extension of the file name. | 93 | // Retrieves the extension of the file name. |
| 93 | virtual std::string GetExtension() const; | 94 | virtual std::string GetExtension() const; |
| 94 | // Retrieves the size of the file. | 95 | // Retrieves the size of the file. |
| 95 | virtual size_t GetSize() const = 0; | 96 | virtual std::size_t GetSize() const = 0; |
| 96 | // Resizes the file to new_size. Returns whether or not the operation was successful. | 97 | // Resizes the file to new_size. Returns whether or not the operation was successful. |
| 97 | virtual bool Resize(size_t new_size) = 0; | 98 | virtual bool Resize(std::size_t new_size) = 0; |
| 98 | // Gets a pointer to the directory containing this file, returning nullptr if there is none. | 99 | // Gets a pointer to the directory containing this file, returning nullptr if there is none. |
| 99 | virtual std::shared_ptr<VfsDirectory> GetContainingDirectory() const = 0; | 100 | virtual std::shared_ptr<VfsDirectory> GetContainingDirectory() const = 0; |
| 100 | 101 | ||
| @@ -105,15 +106,15 @@ public: | |||
| 105 | 106 | ||
| 106 | // The primary method of reading from the file. Reads length bytes into data starting at offset | 107 | // The primary method of reading from the file. Reads length bytes into data starting at offset |
| 107 | // into file. Returns number of bytes successfully read. | 108 | // into file. Returns number of bytes successfully read. |
| 108 | virtual size_t Read(u8* data, size_t length, size_t offset = 0) const = 0; | 109 | virtual std::size_t Read(u8* data, std::size_t length, std::size_t offset = 0) const = 0; |
| 109 | // The primary method of writing to the file. Writes length bytes from data starting at offset | 110 | // The primary method of writing to the file. Writes length bytes from data starting at offset |
| 110 | // into file. Returns number of bytes successfully written. | 111 | // into file. Returns number of bytes successfully written. |
| 111 | virtual size_t Write(const u8* data, size_t length, size_t offset = 0) = 0; | 112 | virtual std::size_t Write(const u8* data, std::size_t length, std::size_t offset = 0) = 0; |
| 112 | 113 | ||
| 113 | // Reads exactly one byte at the offset provided, returning boost::none on error. | 114 | // Reads exactly one byte at the offset provided, returning boost::none on error. |
| 114 | virtual boost::optional<u8> ReadByte(size_t offset = 0) const; | 115 | virtual boost::optional<u8> ReadByte(std::size_t offset = 0) const; |
| 115 | // Reads size bytes starting at offset in file into a vector. | 116 | // Reads size bytes starting at offset in file into a vector. |
| 116 | virtual std::vector<u8> ReadBytes(size_t size, size_t offset = 0) const; | 117 | virtual std::vector<u8> ReadBytes(std::size_t size, std::size_t offset = 0) const; |
| 117 | // Reads all the bytes from the file into a vector. Equivalent to 'file->Read(file->GetSize(), | 118 | // Reads all the bytes from the file into a vector. Equivalent to 'file->Read(file->GetSize(), |
| 118 | // 0)' | 119 | // 0)' |
| 119 | virtual std::vector<u8> ReadAllBytes() const; | 120 | virtual std::vector<u8> ReadAllBytes() const; |
| @@ -121,7 +122,7 @@ public: | |||
| 121 | // Reads an array of type T, size number_elements starting at offset. | 122 | // Reads an array of type T, size number_elements starting at offset. |
| 122 | // Returns the number of bytes (sizeof(T)*number_elements) read successfully. | 123 | // Returns the number of bytes (sizeof(T)*number_elements) read successfully. |
| 123 | template <typename T> | 124 | template <typename T> |
| 124 | size_t ReadArray(T* data, size_t number_elements, size_t offset = 0) const { | 125 | std::size_t ReadArray(T* data, std::size_t number_elements, std::size_t offset = 0) const { |
| 125 | static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); | 126 | static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); |
| 126 | 127 | ||
| 127 | return Read(reinterpret_cast<u8*>(data), number_elements * sizeof(T), offset); | 128 | return Read(reinterpret_cast<u8*>(data), number_elements * sizeof(T), offset); |
| @@ -130,7 +131,7 @@ public: | |||
| 130 | // Reads size bytes into the memory starting at data starting at offset into the file. | 131 | // Reads size bytes into the memory starting at data starting at offset into the file. |
| 131 | // Returns the number of bytes read successfully. | 132 | // Returns the number of bytes read successfully. |
| 132 | template <typename T> | 133 | template <typename T> |
| 133 | size_t ReadBytes(T* data, size_t size, size_t offset = 0) const { | 134 | std::size_t ReadBytes(T* data, std::size_t size, std::size_t offset = 0) const { |
| 134 | static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); | 135 | static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); |
| 135 | return Read(reinterpret_cast<u8*>(data), size, offset); | 136 | return Read(reinterpret_cast<u8*>(data), size, offset); |
| 136 | } | 137 | } |
| @@ -138,22 +139,22 @@ public: | |||
| 138 | // Reads one object of type T starting at offset in file. | 139 | // Reads one object of type T starting at offset in file. |
| 139 | // Returns the number of bytes read successfully (sizeof(T)). | 140 | // Returns the number of bytes read successfully (sizeof(T)). |
| 140 | template <typename T> | 141 | template <typename T> |
| 141 | size_t ReadObject(T* data, size_t offset = 0) const { | 142 | std::size_t ReadObject(T* data, std::size_t offset = 0) const { |
| 142 | static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); | 143 | static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); |
| 143 | return Read(reinterpret_cast<u8*>(data), sizeof(T), offset); | 144 | return Read(reinterpret_cast<u8*>(data), sizeof(T), offset); |
| 144 | } | 145 | } |
| 145 | 146 | ||
| 146 | // Writes exactly one byte to offset in file and retuns whether or not the byte was written | 147 | // Writes exactly one byte to offset in file and retuns whether or not the byte was written |
| 147 | // successfully. | 148 | // successfully. |
| 148 | virtual bool WriteByte(u8 data, size_t offset = 0); | 149 | virtual bool WriteByte(u8 data, std::size_t offset = 0); |
| 149 | // Writes a vector of bytes to offset in file and returns the number of bytes successfully | 150 | // Writes a vector of bytes to offset in file and returns the number of bytes successfully |
| 150 | // written. | 151 | // written. |
| 151 | virtual size_t WriteBytes(const std::vector<u8>& data, size_t offset = 0); | 152 | virtual std::size_t WriteBytes(const std::vector<u8>& data, std::size_t offset = 0); |
| 152 | 153 | ||
| 153 | // Writes an array of type T, size number_elements to offset in file. | 154 | // Writes an array of type T, size number_elements to offset in file. |
| 154 | // Returns the number of bytes (sizeof(T)*number_elements) written successfully. | 155 | // Returns the number of bytes (sizeof(T)*number_elements) written successfully. |
| 155 | template <typename T> | 156 | template <typename T> |
| 156 | size_t WriteArray(const T* data, size_t number_elements, size_t offset = 0) { | 157 | std::size_t WriteArray(const T* data, std::size_t number_elements, std::size_t offset = 0) { |
| 157 | static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); | 158 | static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); |
| 158 | return Write(data, number_elements * sizeof(T), offset); | 159 | return Write(data, number_elements * sizeof(T), offset); |
| 159 | } | 160 | } |
| @@ -161,7 +162,7 @@ public: | |||
| 161 | // Writes size bytes starting at memory location data to offset in file. | 162 | // Writes size bytes starting at memory location data to offset in file. |
| 162 | // Returns the number of bytes written successfully. | 163 | // Returns the number of bytes written successfully. |
| 163 | template <typename T> | 164 | template <typename T> |
| 164 | size_t WriteBytes(const T* data, size_t size, size_t offset = 0) { | 165 | std::size_t WriteBytes(const T* data, std::size_t size, std::size_t offset = 0) { |
| 165 | static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); | 166 | static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); |
| 166 | return Write(reinterpret_cast<const u8*>(data), size, offset); | 167 | return Write(reinterpret_cast<const u8*>(data), size, offset); |
| 167 | } | 168 | } |
| @@ -169,7 +170,7 @@ public: | |||
| 169 | // Writes one object of type T to offset in file. | 170 | // Writes one object of type T to offset in file. |
| 170 | // Returns the number of bytes written successfully (sizeof(T)). | 171 | // Returns the number of bytes written successfully (sizeof(T)). |
| 171 | template <typename T> | 172 | template <typename T> |
| 172 | size_t WriteObject(const T& data, size_t offset = 0) { | 173 | std::size_t WriteObject(const T& data, std::size_t offset = 0) { |
| 173 | static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); | 174 | static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); |
| 174 | return Write(&data, sizeof(T), offset); | 175 | return Write(&data, sizeof(T), offset); |
| 175 | } | 176 | } |
| @@ -221,7 +222,7 @@ public: | |||
| 221 | // Returns the name of the directory. | 222 | // Returns the name of the directory. |
| 222 | virtual std::string GetName() const = 0; | 223 | virtual std::string GetName() const = 0; |
| 223 | // Returns the total size of all files and subdirectories in this directory. | 224 | // Returns the total size of all files and subdirectories in this directory. |
| 224 | virtual size_t GetSize() const; | 225 | virtual std::size_t GetSize() const; |
| 225 | // Returns the parent directory of this directory. Returns nullptr if this directory is root or | 226 | // Returns the parent directory of this directory. Returns nullptr if this directory is root or |
| 226 | // has no parent. | 227 | // has no parent. |
| 227 | virtual std::shared_ptr<VfsDirectory> GetParentDirectory() const = 0; | 228 | virtual std::shared_ptr<VfsDirectory> GetParentDirectory() const = 0; |
| @@ -265,6 +266,10 @@ public: | |||
| 265 | // dest. | 266 | // dest. |
| 266 | virtual bool Copy(std::string_view src, std::string_view dest); | 267 | virtual bool Copy(std::string_view src, std::string_view dest); |
| 267 | 268 | ||
| 269 | // Gets all of the entries directly in the directory (files and dirs), returning a map between | ||
| 270 | // item name -> type. | ||
| 271 | virtual std::map<std::string, VfsEntryType, std::less<>> GetEntries() const; | ||
| 272 | |||
| 268 | // Interprets the file with name file instead as a directory of type directory. | 273 | // Interprets the file with name file instead as a directory of type directory. |
| 269 | // The directory must have a constructor that takes a single argument of type | 274 | // The directory must have a constructor that takes a single argument of type |
| 270 | // std::shared_ptr<VfsFile>. Allows to reinterpret container files (i.e NCA, zip, XCI, etc) as a | 275 | // std::shared_ptr<VfsFile>. Allows to reinterpret container files (i.e NCA, zip, XCI, etc) as a |
| @@ -310,13 +315,19 @@ public: | |||
| 310 | bool Rename(std::string_view name) override; | 315 | bool Rename(std::string_view name) override; |
| 311 | }; | 316 | }; |
| 312 | 317 | ||
| 313 | // Compare the two files, byte-for-byte, in increments specificed by block_size | 318 | // Compare the two files, byte-for-byte, in increments specified by block_size |
| 314 | bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, size_t block_size = 0x200); | 319 | bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, |
| 320 | std::size_t block_size = 0x1000); | ||
| 315 | 321 | ||
| 316 | // A method that copies the raw data between two different implementations of VirtualFile. If you | 322 | // A method that copies the raw data between two different implementations of VirtualFile. If you |
| 317 | // are using the same implementation, it is probably better to use the Copy method in the parent | 323 | // are using the same implementation, it is probably better to use the Copy method in the parent |
| 318 | // directory of src/dest. | 324 | // directory of src/dest. |
| 319 | bool VfsRawCopy(VirtualFile src, VirtualFile dest); | 325 | bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, std::size_t block_size = 0x1000); |
| 326 | |||
| 327 | // A method that performs a similar function to VfsRawCopy above, but instead copies entire | ||
| 328 | // directories. It suffers the same performance penalties as above and an implementation-specific | ||
| 329 | // Copy should always be preferred. | ||
| 330 | bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, std::size_t block_size = 0x1000); | ||
| 320 | 331 | ||
| 321 | // Checks if the directory at path relative to rel exists. If it does, returns that. If it does not | 332 | // Checks if the directory at path relative to rel exists. If it does, returns that. If it does not |
| 322 | // it attempts to create it and returns the new dir or nullptr on failure. | 333 | // it attempts to create it and returns the new dir or nullptr on failure. |
diff --git a/src/core/file_sys/vfs_concat.cpp b/src/core/file_sys/vfs_concat.cpp index e6bf586a3..16d801c0c 100644 --- a/src/core/file_sys/vfs_concat.cpp +++ b/src/core/file_sys/vfs_concat.cpp | |||
| @@ -5,28 +5,75 @@ | |||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <utility> | 6 | #include <utility> |
| 7 | 7 | ||
| 8 | #include "common/assert.h" | ||
| 8 | #include "core/file_sys/vfs_concat.h" | 9 | #include "core/file_sys/vfs_concat.h" |
| 10 | #include "core/file_sys/vfs_static.h" | ||
| 9 | 11 | ||
| 10 | namespace FileSys { | 12 | namespace FileSys { |
| 11 | 13 | ||
| 12 | VirtualFile ConcatenateFiles(std::vector<VirtualFile> files, std::string name) { | 14 | static bool VerifyConcatenationMapContinuity(const std::map<u64, VirtualFile>& map) { |
| 13 | if (files.empty()) | 15 | const auto last_valid = --map.end(); |
| 14 | return nullptr; | 16 | for (auto iter = map.begin(); iter != last_valid;) { |
| 15 | if (files.size() == 1) | 17 | const auto old = iter++; |
| 16 | return files[0]; | 18 | if (old->first + old->second->GetSize() != iter->first) { |
| 19 | return false; | ||
| 20 | } | ||
| 21 | } | ||
| 17 | 22 | ||
| 18 | return std::shared_ptr<VfsFile>(new ConcatenatedVfsFile(std::move(files), std::move(name))); | 23 | return map.begin()->first == 0; |
| 19 | } | 24 | } |
| 20 | 25 | ||
| 21 | ConcatenatedVfsFile::ConcatenatedVfsFile(std::vector<VirtualFile> files_, std::string name) | 26 | ConcatenatedVfsFile::ConcatenatedVfsFile(std::vector<VirtualFile> files_, std::string name) |
| 22 | : name(std::move(name)) { | 27 | : name(std::move(name)) { |
| 23 | size_t next_offset = 0; | 28 | std::size_t next_offset = 0; |
| 24 | for (const auto& file : files_) { | 29 | for (const auto& file : files_) { |
| 25 | files[next_offset] = file; | 30 | files[next_offset] = file; |
| 26 | next_offset += file->GetSize(); | 31 | next_offset += file->GetSize(); |
| 27 | } | 32 | } |
| 28 | } | 33 | } |
| 29 | 34 | ||
| 35 | ConcatenatedVfsFile::ConcatenatedVfsFile(std::map<u64, VirtualFile> files_, std::string name) | ||
| 36 | : files(std::move(files_)), name(std::move(name)) { | ||
| 37 | ASSERT(VerifyConcatenationMapContinuity(files)); | ||
| 38 | } | ||
| 39 | |||
| 40 | ConcatenatedVfsFile::~ConcatenatedVfsFile() = default; | ||
| 41 | |||
| 42 | VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(std::vector<VirtualFile> files, | ||
| 43 | std::string name) { | ||
| 44 | if (files.empty()) | ||
| 45 | return nullptr; | ||
| 46 | if (files.size() == 1) | ||
| 47 | return files[0]; | ||
| 48 | |||
| 49 | return std::shared_ptr<VfsFile>(new ConcatenatedVfsFile(std::move(files), std::move(name))); | ||
| 50 | } | ||
| 51 | |||
| 52 | VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(u8 filler_byte, | ||
| 53 | std::map<u64, VirtualFile> files, | ||
| 54 | std::string name) { | ||
| 55 | if (files.empty()) | ||
| 56 | return nullptr; | ||
| 57 | if (files.size() == 1) | ||
| 58 | return files.begin()->second; | ||
| 59 | |||
| 60 | const auto last_valid = --files.end(); | ||
| 61 | for (auto iter = files.begin(); iter != last_valid;) { | ||
| 62 | const auto old = iter++; | ||
| 63 | if (old->first + old->second->GetSize() != iter->first) { | ||
| 64 | files.emplace(old->first + old->second->GetSize(), | ||
| 65 | std::make_shared<StaticVfsFile>(filler_byte, iter->first - old->first - | ||
| 66 | old->second->GetSize())); | ||
| 67 | } | ||
| 68 | } | ||
| 69 | |||
| 70 | // Ensure the map starts at offset 0 (start of file), otherwise pad to fill. | ||
| 71 | if (files.begin()->first != 0) | ||
| 72 | files.emplace(0, std::make_shared<StaticVfsFile>(filler_byte, files.begin()->first)); | ||
| 73 | |||
| 74 | return std::shared_ptr<VfsFile>(new ConcatenatedVfsFile(std::move(files), std::move(name))); | ||
| 75 | } | ||
| 76 | |||
| 30 | std::string ConcatenatedVfsFile::GetName() const { | 77 | std::string ConcatenatedVfsFile::GetName() const { |
| 31 | if (files.empty()) | 78 | if (files.empty()) |
| 32 | return ""; | 79 | return ""; |
| @@ -35,13 +82,13 @@ std::string ConcatenatedVfsFile::GetName() const { | |||
| 35 | return files.begin()->second->GetName(); | 82 | return files.begin()->second->GetName(); |
| 36 | } | 83 | } |
| 37 | 84 | ||
| 38 | size_t ConcatenatedVfsFile::GetSize() const { | 85 | std::size_t ConcatenatedVfsFile::GetSize() const { |
| 39 | if (files.empty()) | 86 | if (files.empty()) |
| 40 | return 0; | 87 | return 0; |
| 41 | return files.rbegin()->first + files.rbegin()->second->GetSize(); | 88 | return files.rbegin()->first + files.rbegin()->second->GetSize(); |
| 42 | } | 89 | } |
| 43 | 90 | ||
| 44 | bool ConcatenatedVfsFile::Resize(size_t new_size) { | 91 | bool ConcatenatedVfsFile::Resize(std::size_t new_size) { |
| 45 | return false; | 92 | return false; |
| 46 | } | 93 | } |
| 47 | 94 | ||
| @@ -59,8 +106,8 @@ bool ConcatenatedVfsFile::IsReadable() const { | |||
| 59 | return true; | 106 | return true; |
| 60 | } | 107 | } |
| 61 | 108 | ||
| 62 | size_t ConcatenatedVfsFile::Read(u8* data, size_t length, size_t offset) const { | 109 | std::size_t ConcatenatedVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const { |
| 63 | auto entry = files.end(); | 110 | auto entry = --files.end(); |
| 64 | for (auto iter = files.begin(); iter != files.end(); ++iter) { | 111 | for (auto iter = files.begin(); iter != files.end(); ++iter) { |
| 65 | if (iter->first > offset) { | 112 | if (iter->first > offset) { |
| 66 | entry = --iter; | 113 | entry = --iter; |
| @@ -68,27 +115,25 @@ size_t ConcatenatedVfsFile::Read(u8* data, size_t length, size_t offset) const { | |||
| 68 | } | 115 | } |
| 69 | } | 116 | } |
| 70 | 117 | ||
| 71 | // Check if the entry should be the last one. The loop above will make it end(). | 118 | if (entry->first + entry->second->GetSize() <= offset) |
| 72 | if (entry == files.end() && offset < files.rbegin()->first + files.rbegin()->second->GetSize()) | ||
| 73 | --entry; | ||
| 74 | |||
| 75 | if (entry == files.end()) | ||
| 76 | return 0; | 119 | return 0; |
| 77 | 120 | ||
| 78 | const auto remaining = entry->second->GetSize() + offset - entry->first; | 121 | const auto read_in = |
| 79 | if (length > remaining) { | 122 | std::min<u64>(entry->first + entry->second->GetSize() - offset, entry->second->GetSize()); |
| 80 | return entry->second->Read(data, remaining, offset - entry->first) + | 123 | if (length > read_in) { |
| 81 | Read(data + remaining, length - remaining, offset + remaining); | 124 | return entry->second->Read(data, read_in, offset - entry->first) + |
| 125 | Read(data + read_in, length - read_in, offset + read_in); | ||
| 82 | } | 126 | } |
| 83 | 127 | ||
| 84 | return entry->second->Read(data, length, offset - entry->first); | 128 | return entry->second->Read(data, std::min<u64>(read_in, length), offset - entry->first); |
| 85 | } | 129 | } |
| 86 | 130 | ||
| 87 | size_t ConcatenatedVfsFile::Write(const u8* data, size_t length, size_t offset) { | 131 | std::size_t ConcatenatedVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) { |
| 88 | return 0; | 132 | return 0; |
| 89 | } | 133 | } |
| 90 | 134 | ||
| 91 | bool ConcatenatedVfsFile::Rename(std::string_view name) { | 135 | bool ConcatenatedVfsFile::Rename(std::string_view name) { |
| 92 | return false; | 136 | return false; |
| 93 | } | 137 | } |
| 138 | |||
| 94 | } // namespace FileSys | 139 | } // namespace FileSys |
diff --git a/src/core/file_sys/vfs_concat.h b/src/core/file_sys/vfs_concat.h index 686d32515..c90f9d5d1 100644 --- a/src/core/file_sys/vfs_concat.h +++ b/src/core/file_sys/vfs_concat.h | |||
| @@ -4,37 +4,43 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <map> | ||
| 7 | #include <memory> | 8 | #include <memory> |
| 8 | #include <string_view> | 9 | #include <string_view> |
| 9 | #include <boost/container/flat_map.hpp> | ||
| 10 | #include "core/file_sys/vfs.h" | 10 | #include "core/file_sys/vfs.h" |
| 11 | 11 | ||
| 12 | namespace FileSys { | 12 | namespace FileSys { |
| 13 | 13 | ||
| 14 | // Wrapper function to allow for more efficient handling of files.size() == 0, 1 cases. | ||
| 15 | VirtualFile ConcatenateFiles(std::vector<VirtualFile> files, std::string name = ""); | ||
| 16 | |||
| 17 | // Class that wraps multiple vfs files and concatenates them, making reads seamless. Currently | 14 | // Class that wraps multiple vfs files and concatenates them, making reads seamless. Currently |
| 18 | // read-only. | 15 | // read-only. |
| 19 | class ConcatenatedVfsFile : public VfsFile { | 16 | class ConcatenatedVfsFile : public VfsFile { |
| 20 | friend VirtualFile ConcatenateFiles(std::vector<VirtualFile> files, std::string name); | ||
| 21 | |||
| 22 | ConcatenatedVfsFile(std::vector<VirtualFile> files, std::string name); | 17 | ConcatenatedVfsFile(std::vector<VirtualFile> files, std::string name); |
| 18 | ConcatenatedVfsFile(std::map<u64, VirtualFile> files, std::string name); | ||
| 23 | 19 | ||
| 24 | public: | 20 | public: |
| 21 | ~ConcatenatedVfsFile() override; | ||
| 22 | |||
| 23 | /// Wrapper function to allow for more efficient handling of files.size() == 0, 1 cases. | ||
| 24 | static VirtualFile MakeConcatenatedFile(std::vector<VirtualFile> files, std::string name); | ||
| 25 | |||
| 26 | /// Convenience function that turns a map of offsets to files into a concatenated file, filling | ||
| 27 | /// gaps with a given filler byte. | ||
| 28 | static VirtualFile MakeConcatenatedFile(u8 filler_byte, std::map<u64, VirtualFile> files, | ||
| 29 | std::string name); | ||
| 30 | |||
| 25 | std::string GetName() const override; | 31 | std::string GetName() const override; |
| 26 | size_t GetSize() const override; | 32 | std::size_t GetSize() const override; |
| 27 | bool Resize(size_t new_size) override; | 33 | bool Resize(std::size_t new_size) override; |
| 28 | std::shared_ptr<VfsDirectory> GetContainingDirectory() const override; | 34 | std::shared_ptr<VfsDirectory> GetContainingDirectory() const override; |
| 29 | bool IsWritable() const override; | 35 | bool IsWritable() const override; |
| 30 | bool IsReadable() const override; | 36 | bool IsReadable() const override; |
| 31 | size_t Read(u8* data, size_t length, size_t offset) const override; | 37 | std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; |
| 32 | size_t Write(const u8* data, size_t length, size_t offset) override; | 38 | std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override; |
| 33 | bool Rename(std::string_view name) override; | 39 | bool Rename(std::string_view name) override; |
| 34 | 40 | ||
| 35 | private: | 41 | private: |
| 36 | // Maps starting offset to file -- more efficient. | 42 | // Maps starting offset to file -- more efficient. |
| 37 | boost::container::flat_map<u64, VirtualFile> files; | 43 | std::map<u64, VirtualFile> files; |
| 38 | std::string name; | 44 | std::string name; |
| 39 | }; | 45 | }; |
| 40 | 46 | ||
diff --git a/src/core/file_sys/vfs_layered.cpp b/src/core/file_sys/vfs_layered.cpp new file mode 100644 index 000000000..bfee01725 --- /dev/null +++ b/src/core/file_sys/vfs_layered.cpp | |||
| @@ -0,0 +1,132 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <utility> | ||
| 7 | #include "core/file_sys/vfs_layered.h" | ||
| 8 | |||
| 9 | namespace FileSys { | ||
| 10 | |||
| 11 | LayeredVfsDirectory::LayeredVfsDirectory(std::vector<VirtualDir> dirs, std::string name) | ||
| 12 | : dirs(std::move(dirs)), name(std::move(name)) {} | ||
| 13 | |||
| 14 | LayeredVfsDirectory::~LayeredVfsDirectory() = default; | ||
| 15 | |||
| 16 | VirtualDir LayeredVfsDirectory::MakeLayeredDirectory(std::vector<VirtualDir> dirs, | ||
| 17 | std::string name) { | ||
| 18 | if (dirs.empty()) | ||
| 19 | return nullptr; | ||
| 20 | if (dirs.size() == 1) | ||
| 21 | return dirs[0]; | ||
| 22 | |||
| 23 | return std::shared_ptr<VfsDirectory>(new LayeredVfsDirectory(std::move(dirs), std::move(name))); | ||
| 24 | } | ||
| 25 | |||
| 26 | std::shared_ptr<VfsFile> LayeredVfsDirectory::GetFileRelative(std::string_view path) const { | ||
| 27 | for (const auto& layer : dirs) { | ||
| 28 | const auto file = layer->GetFileRelative(path); | ||
| 29 | if (file != nullptr) | ||
| 30 | return file; | ||
| 31 | } | ||
| 32 | |||
| 33 | return nullptr; | ||
| 34 | } | ||
| 35 | |||
| 36 | std::shared_ptr<VfsDirectory> LayeredVfsDirectory::GetDirectoryRelative( | ||
| 37 | std::string_view path) const { | ||
| 38 | std::vector<VirtualDir> out; | ||
| 39 | for (const auto& layer : dirs) { | ||
| 40 | auto dir = layer->GetDirectoryRelative(path); | ||
| 41 | if (dir != nullptr) | ||
| 42 | out.push_back(std::move(dir)); | ||
| 43 | } | ||
| 44 | |||
| 45 | return MakeLayeredDirectory(std::move(out)); | ||
| 46 | } | ||
| 47 | |||
| 48 | std::shared_ptr<VfsFile> LayeredVfsDirectory::GetFile(std::string_view name) const { | ||
| 49 | return GetFileRelative(name); | ||
| 50 | } | ||
| 51 | |||
| 52 | std::shared_ptr<VfsDirectory> LayeredVfsDirectory::GetSubdirectory(std::string_view name) const { | ||
| 53 | return GetDirectoryRelative(name); | ||
| 54 | } | ||
| 55 | |||
| 56 | std::string LayeredVfsDirectory::GetFullPath() const { | ||
| 57 | return dirs[0]->GetFullPath(); | ||
| 58 | } | ||
| 59 | |||
| 60 | std::vector<std::shared_ptr<VfsFile>> LayeredVfsDirectory::GetFiles() const { | ||
| 61 | std::vector<VirtualFile> out; | ||
| 62 | for (const auto& layer : dirs) { | ||
| 63 | for (const auto& file : layer->GetFiles()) { | ||
| 64 | if (std::find_if(out.begin(), out.end(), [&file](const VirtualFile& comp) { | ||
| 65 | return comp->GetName() == file->GetName(); | ||
| 66 | }) == out.end()) { | ||
| 67 | out.push_back(file); | ||
| 68 | } | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | return out; | ||
| 73 | } | ||
| 74 | |||
| 75 | std::vector<std::shared_ptr<VfsDirectory>> LayeredVfsDirectory::GetSubdirectories() const { | ||
| 76 | std::vector<std::string> names; | ||
| 77 | for (const auto& layer : dirs) { | ||
| 78 | for (const auto& sd : layer->GetSubdirectories()) { | ||
| 79 | if (std::find(names.begin(), names.end(), sd->GetName()) == names.end()) | ||
| 80 | names.push_back(sd->GetName()); | ||
| 81 | } | ||
| 82 | } | ||
| 83 | |||
| 84 | std::vector<VirtualDir> out; | ||
| 85 | out.reserve(names.size()); | ||
| 86 | for (const auto& subdir : names) | ||
| 87 | out.push_back(GetSubdirectory(subdir)); | ||
| 88 | |||
| 89 | return out; | ||
| 90 | } | ||
| 91 | |||
| 92 | bool LayeredVfsDirectory::IsWritable() const { | ||
| 93 | return false; | ||
| 94 | } | ||
| 95 | |||
| 96 | bool LayeredVfsDirectory::IsReadable() const { | ||
| 97 | return true; | ||
| 98 | } | ||
| 99 | |||
| 100 | std::string LayeredVfsDirectory::GetName() const { | ||
| 101 | return name.empty() ? dirs[0]->GetName() : name; | ||
| 102 | } | ||
| 103 | |||
| 104 | std::shared_ptr<VfsDirectory> LayeredVfsDirectory::GetParentDirectory() const { | ||
| 105 | return dirs[0]->GetParentDirectory(); | ||
| 106 | } | ||
| 107 | |||
| 108 | std::shared_ptr<VfsDirectory> LayeredVfsDirectory::CreateSubdirectory(std::string_view name) { | ||
| 109 | return nullptr; | ||
| 110 | } | ||
| 111 | |||
| 112 | std::shared_ptr<VfsFile> LayeredVfsDirectory::CreateFile(std::string_view name) { | ||
| 113 | return nullptr; | ||
| 114 | } | ||
| 115 | |||
| 116 | bool LayeredVfsDirectory::DeleteSubdirectory(std::string_view name) { | ||
| 117 | return false; | ||
| 118 | } | ||
| 119 | |||
| 120 | bool LayeredVfsDirectory::DeleteFile(std::string_view name) { | ||
| 121 | return false; | ||
| 122 | } | ||
| 123 | |||
| 124 | bool LayeredVfsDirectory::Rename(std::string_view name_) { | ||
| 125 | name = name_; | ||
| 126 | return true; | ||
| 127 | } | ||
| 128 | |||
| 129 | bool LayeredVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { | ||
| 130 | return false; | ||
| 131 | } | ||
| 132 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/vfs_layered.h b/src/core/file_sys/vfs_layered.h new file mode 100644 index 000000000..d85310f57 --- /dev/null +++ b/src/core/file_sys/vfs_layered.h | |||
| @@ -0,0 +1,50 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include "core/file_sys/vfs.h" | ||
| 9 | |||
| 10 | namespace FileSys { | ||
| 11 | |||
| 12 | // Class that stacks multiple VfsDirectories on top of each other, attempting to read from the first | ||
| 13 | // one and falling back to the one after. The highest priority directory (overwrites all others) | ||
| 14 | // should be element 0 in the dirs vector. | ||
| 15 | class LayeredVfsDirectory : public VfsDirectory { | ||
| 16 | LayeredVfsDirectory(std::vector<VirtualDir> dirs, std::string name); | ||
| 17 | |||
| 18 | public: | ||
| 19 | ~LayeredVfsDirectory() override; | ||
| 20 | |||
| 21 | /// Wrapper function to allow for more efficient handling of dirs.size() == 0, 1 cases. | ||
| 22 | static VirtualDir MakeLayeredDirectory(std::vector<VirtualDir> dirs, std::string name = ""); | ||
| 23 | |||
| 24 | std::shared_ptr<VfsFile> GetFileRelative(std::string_view path) const override; | ||
| 25 | std::shared_ptr<VfsDirectory> GetDirectoryRelative(std::string_view path) const override; | ||
| 26 | std::shared_ptr<VfsFile> GetFile(std::string_view name) const override; | ||
| 27 | std::shared_ptr<VfsDirectory> GetSubdirectory(std::string_view name) const override; | ||
| 28 | std::string GetFullPath() const override; | ||
| 29 | |||
| 30 | std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; | ||
| 31 | std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override; | ||
| 32 | bool IsWritable() const override; | ||
| 33 | bool IsReadable() const override; | ||
| 34 | std::string GetName() const override; | ||
| 35 | std::shared_ptr<VfsDirectory> GetParentDirectory() const override; | ||
| 36 | std::shared_ptr<VfsDirectory> CreateSubdirectory(std::string_view name) override; | ||
| 37 | std::shared_ptr<VfsFile> CreateFile(std::string_view name) override; | ||
| 38 | bool DeleteSubdirectory(std::string_view name) override; | ||
| 39 | bool DeleteFile(std::string_view name) override; | ||
| 40 | bool Rename(std::string_view name) override; | ||
| 41 | |||
| 42 | protected: | ||
| 43 | bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; | ||
| 44 | |||
| 45 | private: | ||
| 46 | std::vector<VirtualDir> dirs; | ||
| 47 | std::string name; | ||
| 48 | }; | ||
| 49 | |||
| 50 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/vfs_offset.cpp b/src/core/file_sys/vfs_offset.cpp index 847cde2f5..a4c6719a0 100644 --- a/src/core/file_sys/vfs_offset.cpp +++ b/src/core/file_sys/vfs_offset.cpp | |||
| @@ -9,20 +9,22 @@ | |||
| 9 | 9 | ||
| 10 | namespace FileSys { | 10 | namespace FileSys { |
| 11 | 11 | ||
| 12 | OffsetVfsFile::OffsetVfsFile(std::shared_ptr<VfsFile> file_, size_t size_, size_t offset_, | 12 | OffsetVfsFile::OffsetVfsFile(std::shared_ptr<VfsFile> file_, std::size_t size_, std::size_t offset_, |
| 13 | std::string name_, VirtualDir parent_) | 13 | std::string name_, VirtualDir parent_) |
| 14 | : file(file_), offset(offset_), size(size_), name(std::move(name_)), | 14 | : file(file_), offset(offset_), size(size_), name(std::move(name_)), |
| 15 | parent(parent_ == nullptr ? file->GetContainingDirectory() : std::move(parent_)) {} | 15 | parent(parent_ == nullptr ? file->GetContainingDirectory() : std::move(parent_)) {} |
| 16 | 16 | ||
| 17 | OffsetVfsFile::~OffsetVfsFile() = default; | ||
| 18 | |||
| 17 | std::string OffsetVfsFile::GetName() const { | 19 | std::string OffsetVfsFile::GetName() const { |
| 18 | return name.empty() ? file->GetName() : name; | 20 | return name.empty() ? file->GetName() : name; |
| 19 | } | 21 | } |
| 20 | 22 | ||
| 21 | size_t OffsetVfsFile::GetSize() const { | 23 | std::size_t OffsetVfsFile::GetSize() const { |
| 22 | return size; | 24 | return size; |
| 23 | } | 25 | } |
| 24 | 26 | ||
| 25 | bool OffsetVfsFile::Resize(size_t new_size) { | 27 | bool OffsetVfsFile::Resize(std::size_t new_size) { |
| 26 | if (offset + new_size < file->GetSize()) { | 28 | if (offset + new_size < file->GetSize()) { |
| 27 | size = new_size; | 29 | size = new_size; |
| 28 | } else { | 30 | } else { |
| @@ -47,22 +49,22 @@ bool OffsetVfsFile::IsReadable() const { | |||
| 47 | return file->IsReadable(); | 49 | return file->IsReadable(); |
| 48 | } | 50 | } |
| 49 | 51 | ||
| 50 | size_t OffsetVfsFile::Read(u8* data, size_t length, size_t r_offset) const { | 52 | std::size_t OffsetVfsFile::Read(u8* data, std::size_t length, std::size_t r_offset) const { |
| 51 | return file->Read(data, TrimToFit(length, r_offset), offset + r_offset); | 53 | return file->Read(data, TrimToFit(length, r_offset), offset + r_offset); |
| 52 | } | 54 | } |
| 53 | 55 | ||
| 54 | size_t OffsetVfsFile::Write(const u8* data, size_t length, size_t r_offset) { | 56 | std::size_t OffsetVfsFile::Write(const u8* data, std::size_t length, std::size_t r_offset) { |
| 55 | return file->Write(data, TrimToFit(length, r_offset), offset + r_offset); | 57 | return file->Write(data, TrimToFit(length, r_offset), offset + r_offset); |
| 56 | } | 58 | } |
| 57 | 59 | ||
| 58 | boost::optional<u8> OffsetVfsFile::ReadByte(size_t r_offset) const { | 60 | boost::optional<u8> OffsetVfsFile::ReadByte(std::size_t r_offset) const { |
| 59 | if (r_offset < size) | 61 | if (r_offset < size) |
| 60 | return file->ReadByte(offset + r_offset); | 62 | return file->ReadByte(offset + r_offset); |
| 61 | 63 | ||
| 62 | return boost::none; | 64 | return boost::none; |
| 63 | } | 65 | } |
| 64 | 66 | ||
| 65 | std::vector<u8> OffsetVfsFile::ReadBytes(size_t r_size, size_t r_offset) const { | 67 | std::vector<u8> OffsetVfsFile::ReadBytes(std::size_t r_size, std::size_t r_offset) const { |
| 66 | return file->ReadBytes(TrimToFit(r_size, r_offset), offset + r_offset); | 68 | return file->ReadBytes(TrimToFit(r_size, r_offset), offset + r_offset); |
| 67 | } | 69 | } |
| 68 | 70 | ||
| @@ -70,14 +72,14 @@ std::vector<u8> OffsetVfsFile::ReadAllBytes() const { | |||
| 70 | return file->ReadBytes(size, offset); | 72 | return file->ReadBytes(size, offset); |
| 71 | } | 73 | } |
| 72 | 74 | ||
| 73 | bool OffsetVfsFile::WriteByte(u8 data, size_t r_offset) { | 75 | bool OffsetVfsFile::WriteByte(u8 data, std::size_t r_offset) { |
| 74 | if (r_offset < size) | 76 | if (r_offset < size) |
| 75 | return file->WriteByte(data, offset + r_offset); | 77 | return file->WriteByte(data, offset + r_offset); |
| 76 | 78 | ||
| 77 | return false; | 79 | return false; |
| 78 | } | 80 | } |
| 79 | 81 | ||
| 80 | size_t OffsetVfsFile::WriteBytes(const std::vector<u8>& data, size_t r_offset) { | 82 | std::size_t OffsetVfsFile::WriteBytes(const std::vector<u8>& data, std::size_t r_offset) { |
| 81 | return file->Write(data.data(), TrimToFit(data.size(), r_offset), offset + r_offset); | 83 | return file->Write(data.data(), TrimToFit(data.size(), r_offset), offset + r_offset); |
| 82 | } | 84 | } |
| 83 | 85 | ||
| @@ -85,12 +87,12 @@ bool OffsetVfsFile::Rename(std::string_view name) { | |||
| 85 | return file->Rename(name); | 87 | return file->Rename(name); |
| 86 | } | 88 | } |
| 87 | 89 | ||
| 88 | size_t OffsetVfsFile::GetOffset() const { | 90 | std::size_t OffsetVfsFile::GetOffset() const { |
| 89 | return offset; | 91 | return offset; |
| 90 | } | 92 | } |
| 91 | 93 | ||
| 92 | size_t OffsetVfsFile::TrimToFit(size_t r_size, size_t r_offset) const { | 94 | std::size_t OffsetVfsFile::TrimToFit(std::size_t r_size, std::size_t r_offset) const { |
| 93 | return std::clamp(r_size, size_t{0}, size - r_offset); | 95 | return std::clamp(r_size, std::size_t{0}, size - r_offset); |
| 94 | } | 96 | } |
| 95 | 97 | ||
| 96 | } // namespace FileSys | 98 | } // namespace FileSys |
diff --git a/src/core/file_sys/vfs_offset.h b/src/core/file_sys/vfs_offset.h index cb92d1570..8062702a7 100644 --- a/src/core/file_sys/vfs_offset.h +++ b/src/core/file_sys/vfs_offset.h | |||
| @@ -17,33 +17,34 @@ namespace FileSys { | |||
| 17 | // the size of this wrapper. | 17 | // the size of this wrapper. |
| 18 | class OffsetVfsFile : public VfsFile { | 18 | class OffsetVfsFile : public VfsFile { |
| 19 | public: | 19 | public: |
| 20 | OffsetVfsFile(std::shared_ptr<VfsFile> file, size_t size, size_t offset = 0, | 20 | OffsetVfsFile(std::shared_ptr<VfsFile> file, std::size_t size, std::size_t offset = 0, |
| 21 | std::string new_name = "", VirtualDir new_parent = nullptr); | 21 | std::string new_name = "", VirtualDir new_parent = nullptr); |
| 22 | ~OffsetVfsFile() override; | ||
| 22 | 23 | ||
| 23 | std::string GetName() const override; | 24 | std::string GetName() const override; |
| 24 | size_t GetSize() const override; | 25 | std::size_t GetSize() const override; |
| 25 | bool Resize(size_t new_size) override; | 26 | bool Resize(std::size_t new_size) override; |
| 26 | std::shared_ptr<VfsDirectory> GetContainingDirectory() const override; | 27 | std::shared_ptr<VfsDirectory> GetContainingDirectory() const override; |
| 27 | bool IsWritable() const override; | 28 | bool IsWritable() const override; |
| 28 | bool IsReadable() const override; | 29 | bool IsReadable() const override; |
| 29 | size_t Read(u8* data, size_t length, size_t offset) const override; | 30 | std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; |
| 30 | size_t Write(const u8* data, size_t length, size_t offset) override; | 31 | std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override; |
| 31 | boost::optional<u8> ReadByte(size_t offset) const override; | 32 | boost::optional<u8> ReadByte(std::size_t offset) const override; |
| 32 | std::vector<u8> ReadBytes(size_t size, size_t offset) const override; | 33 | std::vector<u8> ReadBytes(std::size_t size, std::size_t offset) const override; |
| 33 | std::vector<u8> ReadAllBytes() const override; | 34 | std::vector<u8> ReadAllBytes() const override; |
| 34 | bool WriteByte(u8 data, size_t offset) override; | 35 | bool WriteByte(u8 data, std::size_t offset) override; |
| 35 | size_t WriteBytes(const std::vector<u8>& data, size_t offset) override; | 36 | std::size_t WriteBytes(const std::vector<u8>& data, std::size_t offset) override; |
| 36 | 37 | ||
| 37 | bool Rename(std::string_view name) override; | 38 | bool Rename(std::string_view name) override; |
| 38 | 39 | ||
| 39 | size_t GetOffset() const; | 40 | std::size_t GetOffset() const; |
| 40 | 41 | ||
| 41 | private: | 42 | private: |
| 42 | size_t TrimToFit(size_t r_size, size_t r_offset) const; | 43 | std::size_t TrimToFit(std::size_t r_size, std::size_t r_offset) const; |
| 43 | 44 | ||
| 44 | std::shared_ptr<VfsFile> file; | 45 | std::shared_ptr<VfsFile> file; |
| 45 | size_t offset; | 46 | std::size_t offset; |
| 46 | size_t size; | 47 | std::size_t size; |
| 47 | std::string name; | 48 | std::string name; |
| 48 | VirtualDir parent; | 49 | VirtualDir parent; |
| 49 | }; | 50 | }; |
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp index 89b101145..9defad04c 100644 --- a/src/core/file_sys/vfs_real.cpp +++ b/src/core/file_sys/vfs_real.cpp | |||
| @@ -227,11 +227,11 @@ std::string RealVfsFile::GetName() const { | |||
| 227 | return path_components.back(); | 227 | return path_components.back(); |
| 228 | } | 228 | } |
| 229 | 229 | ||
| 230 | size_t RealVfsFile::GetSize() const { | 230 | std::size_t RealVfsFile::GetSize() const { |
| 231 | return backing->GetSize(); | 231 | return backing->GetSize(); |
| 232 | } | 232 | } |
| 233 | 233 | ||
| 234 | bool RealVfsFile::Resize(size_t new_size) { | 234 | bool RealVfsFile::Resize(std::size_t new_size) { |
| 235 | return backing->Resize(new_size); | 235 | return backing->Resize(new_size); |
| 236 | } | 236 | } |
| 237 | 237 | ||
| @@ -247,13 +247,13 @@ bool RealVfsFile::IsReadable() const { | |||
| 247 | return (perms & Mode::ReadWrite) != 0; | 247 | return (perms & Mode::ReadWrite) != 0; |
| 248 | } | 248 | } |
| 249 | 249 | ||
| 250 | size_t RealVfsFile::Read(u8* data, size_t length, size_t offset) const { | 250 | std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const { |
| 251 | if (!backing->Seek(offset, SEEK_SET)) | 251 | if (!backing->Seek(offset, SEEK_SET)) |
| 252 | return 0; | 252 | return 0; |
| 253 | return backing->ReadBytes(data, length); | 253 | return backing->ReadBytes(data, length); |
| 254 | } | 254 | } |
| 255 | 255 | ||
| 256 | size_t RealVfsFile::Write(const u8* data, size_t length, size_t offset) { | 256 | std::size_t RealVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) { |
| 257 | if (!backing->Seek(offset, SEEK_SET)) | 257 | if (!backing->Seek(offset, SEEK_SET)) |
| 258 | return 0; | 258 | return 0; |
| 259 | return backing->WriteBytes(data, length); | 259 | return backing->WriteBytes(data, length); |
| @@ -413,6 +413,23 @@ std::string RealVfsDirectory::GetFullPath() const { | |||
| 413 | return out; | 413 | return out; |
| 414 | } | 414 | } |
| 415 | 415 | ||
| 416 | std::map<std::string, VfsEntryType, std::less<>> RealVfsDirectory::GetEntries() const { | ||
| 417 | if (perms == Mode::Append) | ||
| 418 | return {}; | ||
| 419 | |||
| 420 | std::map<std::string, VfsEntryType, std::less<>> out; | ||
| 421 | FileUtil::ForeachDirectoryEntry( | ||
| 422 | nullptr, path, | ||
| 423 | [&out](u64* entries_out, const std::string& directory, const std::string& filename) { | ||
| 424 | const std::string full_path = directory + DIR_SEP + filename; | ||
| 425 | out.emplace(filename, FileUtil::IsDirectory(full_path) ? VfsEntryType::Directory | ||
| 426 | : VfsEntryType::File); | ||
| 427 | return true; | ||
| 428 | }); | ||
| 429 | |||
| 430 | return out; | ||
| 431 | } | ||
| 432 | |||
| 416 | bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { | 433 | bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { |
| 417 | return false; | 434 | return false; |
| 418 | } | 435 | } |
diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h index 7db86691f..5b61db90d 100644 --- a/src/core/file_sys/vfs_real.h +++ b/src/core/file_sys/vfs_real.h | |||
| @@ -48,13 +48,13 @@ public: | |||
| 48 | ~RealVfsFile() override; | 48 | ~RealVfsFile() override; |
| 49 | 49 | ||
| 50 | std::string GetName() const override; | 50 | std::string GetName() const override; |
| 51 | size_t GetSize() const override; | 51 | std::size_t GetSize() const override; |
| 52 | bool Resize(size_t new_size) override; | 52 | bool Resize(std::size_t new_size) override; |
| 53 | std::shared_ptr<VfsDirectory> GetContainingDirectory() const override; | 53 | std::shared_ptr<VfsDirectory> GetContainingDirectory() const override; |
| 54 | bool IsWritable() const override; | 54 | bool IsWritable() const override; |
| 55 | bool IsReadable() const override; | 55 | bool IsReadable() const override; |
| 56 | size_t Read(u8* data, size_t length, size_t offset) const override; | 56 | std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; |
| 57 | size_t Write(const u8* data, size_t length, size_t offset) override; | 57 | std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override; |
| 58 | bool Rename(std::string_view name) override; | 58 | bool Rename(std::string_view name) override; |
| 59 | 59 | ||
| 60 | private: | 60 | private: |
| @@ -98,6 +98,7 @@ public: | |||
| 98 | bool DeleteFile(std::string_view name) override; | 98 | bool DeleteFile(std::string_view name) override; |
| 99 | bool Rename(std::string_view name) override; | 99 | bool Rename(std::string_view name) override; |
| 100 | std::string GetFullPath() const override; | 100 | std::string GetFullPath() const override; |
| 101 | std::map<std::string, VfsEntryType, std::less<>> GetEntries() const override; | ||
| 101 | 102 | ||
| 102 | protected: | 103 | protected: |
| 103 | bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; | 104 | bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; |
diff --git a/src/core/file_sys/vfs_static.h b/src/core/file_sys/vfs_static.h new file mode 100644 index 000000000..44fab51d1 --- /dev/null +++ b/src/core/file_sys/vfs_static.h | |||
| @@ -0,0 +1,79 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <algorithm> | ||
| 8 | #include <memory> | ||
| 9 | #include <string_view> | ||
| 10 | |||
| 11 | #include "core/file_sys/vfs.h" | ||
| 12 | |||
| 13 | namespace FileSys { | ||
| 14 | |||
| 15 | class StaticVfsFile : public VfsFile { | ||
| 16 | public: | ||
| 17 | explicit StaticVfsFile(u8 value, std::size_t size = 0, std::string name = "", | ||
| 18 | VirtualDir parent = nullptr) | ||
| 19 | : value{value}, size{size}, name{std::move(name)}, parent{std::move(parent)} {} | ||
| 20 | |||
| 21 | std::string GetName() const override { | ||
| 22 | return name; | ||
| 23 | } | ||
| 24 | |||
| 25 | std::size_t GetSize() const override { | ||
| 26 | return size; | ||
| 27 | } | ||
| 28 | |||
| 29 | bool Resize(std::size_t new_size) override { | ||
| 30 | size = new_size; | ||
| 31 | return true; | ||
| 32 | } | ||
| 33 | |||
| 34 | std::shared_ptr<VfsDirectory> GetContainingDirectory() const override { | ||
| 35 | return parent; | ||
| 36 | } | ||
| 37 | |||
| 38 | bool IsWritable() const override { | ||
| 39 | return false; | ||
| 40 | } | ||
| 41 | |||
| 42 | bool IsReadable() const override { | ||
| 43 | return true; | ||
| 44 | } | ||
| 45 | |||
| 46 | std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override { | ||
| 47 | const auto read = std::min(length, size - offset); | ||
| 48 | std::fill(data, data + read, value); | ||
| 49 | return read; | ||
| 50 | } | ||
| 51 | |||
| 52 | std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override { | ||
| 53 | return 0; | ||
| 54 | } | ||
| 55 | |||
| 56 | boost::optional<u8> ReadByte(std::size_t offset) const override { | ||
| 57 | if (offset < size) | ||
| 58 | return value; | ||
| 59 | return boost::none; | ||
| 60 | } | ||
| 61 | |||
| 62 | std::vector<u8> ReadBytes(std::size_t length, std::size_t offset) const override { | ||
| 63 | const auto read = std::min(length, size - offset); | ||
| 64 | return std::vector<u8>(read, value); | ||
| 65 | } | ||
| 66 | |||
| 67 | bool Rename(std::string_view new_name) override { | ||
| 68 | name = new_name; | ||
| 69 | return true; | ||
| 70 | } | ||
| 71 | |||
| 72 | private: | ||
| 73 | u8 value; | ||
| 74 | std::size_t size; | ||
| 75 | std::string name; | ||
| 76 | VirtualDir parent; | ||
| 77 | }; | ||
| 78 | |||
| 79 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/vfs_vector.cpp b/src/core/file_sys/vfs_vector.cpp index 98e7c4598..389c7e003 100644 --- a/src/core/file_sys/vfs_vector.cpp +++ b/src/core/file_sys/vfs_vector.cpp | |||
| @@ -3,16 +3,72 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <cstring> | ||
| 6 | #include <utility> | 7 | #include <utility> |
| 7 | #include "core/file_sys/vfs_vector.h" | 8 | #include "core/file_sys/vfs_vector.h" |
| 8 | 9 | ||
| 9 | namespace FileSys { | 10 | namespace FileSys { |
| 11 | VectorVfsFile::VectorVfsFile(std::vector<u8> initial_data, std::string name, VirtualDir parent) | ||
| 12 | : data(std::move(initial_data)), parent(std::move(parent)), name(std::move(name)) {} | ||
| 13 | |||
| 14 | VectorVfsFile::~VectorVfsFile() = default; | ||
| 15 | |||
| 16 | std::string VectorVfsFile::GetName() const { | ||
| 17 | return name; | ||
| 18 | } | ||
| 19 | |||
| 20 | size_t VectorVfsFile::GetSize() const { | ||
| 21 | return data.size(); | ||
| 22 | } | ||
| 23 | |||
| 24 | bool VectorVfsFile::Resize(size_t new_size) { | ||
| 25 | data.resize(new_size); | ||
| 26 | return true; | ||
| 27 | } | ||
| 28 | |||
| 29 | std::shared_ptr<VfsDirectory> VectorVfsFile::GetContainingDirectory() const { | ||
| 30 | return parent; | ||
| 31 | } | ||
| 32 | |||
| 33 | bool VectorVfsFile::IsWritable() const { | ||
| 34 | return true; | ||
| 35 | } | ||
| 36 | |||
| 37 | bool VectorVfsFile::IsReadable() const { | ||
| 38 | return true; | ||
| 39 | } | ||
| 40 | |||
| 41 | std::size_t VectorVfsFile::Read(u8* data_, std::size_t length, std::size_t offset) const { | ||
| 42 | const auto read = std::min(length, data.size() - offset); | ||
| 43 | std::memcpy(data_, data.data() + offset, read); | ||
| 44 | return read; | ||
| 45 | } | ||
| 46 | |||
| 47 | std::size_t VectorVfsFile::Write(const u8* data_, std::size_t length, std::size_t offset) { | ||
| 48 | if (offset + length > data.size()) | ||
| 49 | data.resize(offset + length); | ||
| 50 | const auto write = std::min(length, data.size() - offset); | ||
| 51 | std::memcpy(data.data(), data_, write); | ||
| 52 | return write; | ||
| 53 | } | ||
| 54 | |||
| 55 | bool VectorVfsFile::Rename(std::string_view name_) { | ||
| 56 | name = name_; | ||
| 57 | return true; | ||
| 58 | } | ||
| 59 | |||
| 60 | void VectorVfsFile::Assign(std::vector<u8> new_data) { | ||
| 61 | data = std::move(new_data); | ||
| 62 | } | ||
| 63 | |||
| 10 | VectorVfsDirectory::VectorVfsDirectory(std::vector<VirtualFile> files_, | 64 | VectorVfsDirectory::VectorVfsDirectory(std::vector<VirtualFile> files_, |
| 11 | std::vector<VirtualDir> dirs_, std::string name_, | 65 | std::vector<VirtualDir> dirs_, std::string name_, |
| 12 | VirtualDir parent_) | 66 | VirtualDir parent_) |
| 13 | : files(std::move(files_)), dirs(std::move(dirs_)), parent(std::move(parent_)), | 67 | : files(std::move(files_)), dirs(std::move(dirs_)), parent(std::move(parent_)), |
| 14 | name(std::move(name_)) {} | 68 | name(std::move(name_)) {} |
| 15 | 69 | ||
| 70 | VectorVfsDirectory::~VectorVfsDirectory() = default; | ||
| 71 | |||
| 16 | std::vector<std::shared_ptr<VfsFile>> VectorVfsDirectory::GetFiles() const { | 72 | std::vector<std::shared_ptr<VfsFile>> VectorVfsDirectory::GetFiles() const { |
| 17 | return files; | 73 | return files; |
| 18 | } | 74 | } |
diff --git a/src/core/file_sys/vfs_vector.h b/src/core/file_sys/vfs_vector.h index 179f62e4b..48a414c98 100644 --- a/src/core/file_sys/vfs_vector.h +++ b/src/core/file_sys/vfs_vector.h | |||
| @@ -8,6 +8,31 @@ | |||
| 8 | 8 | ||
| 9 | namespace FileSys { | 9 | namespace FileSys { |
| 10 | 10 | ||
| 11 | // An implementation of VfsFile that is backed by a vector optionally supplied upon construction | ||
| 12 | class VectorVfsFile : public VfsFile { | ||
| 13 | public: | ||
| 14 | explicit VectorVfsFile(std::vector<u8> initial_data = {}, std::string name = "", | ||
| 15 | VirtualDir parent = nullptr); | ||
| 16 | ~VectorVfsFile() override; | ||
| 17 | |||
| 18 | std::string GetName() const override; | ||
| 19 | std::size_t GetSize() const override; | ||
| 20 | bool Resize(std::size_t new_size) override; | ||
| 21 | std::shared_ptr<VfsDirectory> GetContainingDirectory() const override; | ||
| 22 | bool IsWritable() const override; | ||
| 23 | bool IsReadable() const override; | ||
| 24 | std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override; | ||
| 25 | std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override; | ||
| 26 | bool Rename(std::string_view name) override; | ||
| 27 | |||
| 28 | virtual void Assign(std::vector<u8> new_data); | ||
| 29 | |||
| 30 | private: | ||
| 31 | std::vector<u8> data; | ||
| 32 | VirtualDir parent; | ||
| 33 | std::string name; | ||
| 34 | }; | ||
| 35 | |||
| 11 | // An implementation of VfsDirectory that maintains two vectors for subdirectories and files. | 36 | // An implementation of VfsDirectory that maintains two vectors for subdirectories and files. |
| 12 | // Vector data is supplied upon construction. | 37 | // Vector data is supplied upon construction. |
| 13 | class VectorVfsDirectory : public VfsDirectory { | 38 | class VectorVfsDirectory : public VfsDirectory { |
| @@ -15,6 +40,7 @@ public: | |||
| 15 | explicit VectorVfsDirectory(std::vector<VirtualFile> files = {}, | 40 | explicit VectorVfsDirectory(std::vector<VirtualFile> files = {}, |
| 16 | std::vector<VirtualDir> dirs = {}, std::string name = "", | 41 | std::vector<VirtualDir> dirs = {}, std::string name = "", |
| 17 | VirtualDir parent = nullptr); | 42 | VirtualDir parent = nullptr); |
| 43 | ~VectorVfsDirectory() override; | ||
| 18 | 44 | ||
| 19 | std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; | 45 | std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; |
| 20 | std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override; | 46 | std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override; |
diff --git a/src/core/file_sys/xts_archive.cpp b/src/core/file_sys/xts_archive.cpp index 4dbc25c55..b2b164368 100644 --- a/src/core/file_sys/xts_archive.cpp +++ b/src/core/file_sys/xts_archive.cpp | |||
| @@ -25,14 +25,11 @@ namespace FileSys { | |||
| 25 | constexpr u64 NAX_HEADER_PADDING_SIZE = 0x4000; | 25 | constexpr u64 NAX_HEADER_PADDING_SIZE = 0x4000; |
| 26 | 26 | ||
| 27 | template <typename SourceData, typename SourceKey, typename Destination> | 27 | template <typename SourceData, typename SourceKey, typename Destination> |
| 28 | static bool CalculateHMAC256(Destination* out, const SourceKey* key, size_t key_length, | 28 | static bool CalculateHMAC256(Destination* out, const SourceKey* key, std::size_t key_length, |
| 29 | const SourceData* data, size_t data_length) { | 29 | const SourceData* data, std::size_t data_length) { |
| 30 | mbedtls_md_context_t context; | 30 | mbedtls_md_context_t context; |
| 31 | mbedtls_md_init(&context); | 31 | mbedtls_md_init(&context); |
| 32 | 32 | ||
| 33 | const auto key_f = reinterpret_cast<const u8*>(key); | ||
| 34 | const std::vector<u8> key_v(key_f, key_f + key_length); | ||
| 35 | |||
| 36 | if (mbedtls_md_setup(&context, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 1) || | 33 | if (mbedtls_md_setup(&context, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 1) || |
| 37 | mbedtls_md_hmac_starts(&context, reinterpret_cast<const u8*>(key), key_length) || | 34 | mbedtls_md_hmac_starts(&context, reinterpret_cast<const u8*>(key), key_length) || |
| 38 | mbedtls_md_hmac_update(&context, reinterpret_cast<const u8*>(data), data_length) || | 35 | mbedtls_md_hmac_update(&context, reinterpret_cast<const u8*>(data), data_length) || |
| @@ -45,7 +42,7 @@ static bool CalculateHMAC256(Destination* out, const SourceKey* key, size_t key_ | |||
| 45 | return true; | 42 | return true; |
| 46 | } | 43 | } |
| 47 | 44 | ||
| 48 | NAX::NAX(VirtualFile file_) : file(std::move(file_)), header(std::make_unique<NAXHeader>()) { | 45 | NAX::NAX(VirtualFile file_) : header(std::make_unique<NAXHeader>()), file(std::move(file_)) { |
| 49 | std::string path = FileUtil::SanitizePath(file->GetFullPath()); | 46 | std::string path = FileUtil::SanitizePath(file->GetFullPath()); |
| 50 | static const std::regex nax_path_regex("/registered/(000000[0-9A-F]{2})/([0-9A-F]{32})\\.nca", | 47 | static const std::regex nax_path_regex("/registered/(000000[0-9A-F]{2})/([0-9A-F]{32})\\.nca", |
| 51 | std::regex_constants::ECMAScript | | 48 | std::regex_constants::ECMAScript | |
| @@ -65,13 +62,15 @@ NAX::NAX(VirtualFile file_) : file(std::move(file_)), header(std::make_unique<NA | |||
| 65 | } | 62 | } |
| 66 | 63 | ||
| 67 | NAX::NAX(VirtualFile file_, std::array<u8, 0x10> nca_id) | 64 | NAX::NAX(VirtualFile file_, std::array<u8, 0x10> nca_id) |
| 68 | : file(std::move(file_)), header(std::make_unique<NAXHeader>()) { | 65 | : header(std::make_unique<NAXHeader>()), file(std::move(file_)) { |
| 69 | Core::Crypto::SHA256Hash hash{}; | 66 | Core::Crypto::SHA256Hash hash{}; |
| 70 | mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0); | 67 | mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0); |
| 71 | status = Parse(fmt::format("/registered/000000{:02X}/{}.nca", hash[0], | 68 | status = Parse(fmt::format("/registered/000000{:02X}/{}.nca", hash[0], |
| 72 | Common::HexArrayToString(nca_id, false))); | 69 | Common::HexArrayToString(nca_id, false))); |
| 73 | } | 70 | } |
| 74 | 71 | ||
| 72 | NAX::~NAX() = default; | ||
| 73 | |||
| 75 | Loader::ResultStatus NAX::Parse(std::string_view path) { | 74 | Loader::ResultStatus NAX::Parse(std::string_view path) { |
| 76 | if (file->ReadObject(header.get()) != sizeof(NAXHeader)) | 75 | if (file->ReadObject(header.get()) != sizeof(NAXHeader)) |
| 77 | return Loader::ResultStatus::ErrorBadNAXHeader; | 76 | return Loader::ResultStatus::ErrorBadNAXHeader; |
| @@ -91,7 +90,7 @@ Loader::ResultStatus NAX::Parse(std::string_view path) { | |||
| 91 | 90 | ||
| 92 | const auto enc_keys = header->key_area; | 91 | const auto enc_keys = header->key_area; |
| 93 | 92 | ||
| 94 | size_t i = 0; | 93 | std::size_t i = 0; |
| 95 | for (; i < sd_keys.size(); ++i) { | 94 | for (; i < sd_keys.size(); ++i) { |
| 96 | std::array<Core::Crypto::Key128, 2> nax_keys{}; | 95 | std::array<Core::Crypto::Key128, 2> nax_keys{}; |
| 97 | if (!CalculateHMAC256(nax_keys.data(), sd_keys[i].data(), 0x10, std::string(path).c_str(), | 96 | if (!CalculateHMAC256(nax_keys.data(), sd_keys[i].data(), 0x10, std::string(path).c_str(), |
| @@ -99,7 +98,7 @@ Loader::ResultStatus NAX::Parse(std::string_view path) { | |||
| 99 | return Loader::ResultStatus::ErrorNAXKeyHMACFailed; | 98 | return Loader::ResultStatus::ErrorNAXKeyHMACFailed; |
| 100 | } | 99 | } |
| 101 | 100 | ||
| 102 | for (size_t j = 0; j < nax_keys.size(); ++j) { | 101 | for (std::size_t j = 0; j < nax_keys.size(); ++j) { |
| 103 | Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(nax_keys[j], | 102 | Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(nax_keys[j], |
| 104 | Core::Crypto::Mode::ECB); | 103 | Core::Crypto::Mode::ECB); |
| 105 | cipher.Transcode(enc_keys[j].data(), 0x10, header->key_area[j].data(), | 104 | cipher.Transcode(enc_keys[j].data(), 0x10, header->key_area[j].data(), |
| @@ -138,9 +137,9 @@ VirtualFile NAX::GetDecrypted() const { | |||
| 138 | return dec_file; | 137 | return dec_file; |
| 139 | } | 138 | } |
| 140 | 139 | ||
| 141 | std::shared_ptr<NCA> NAX::AsNCA() const { | 140 | std::unique_ptr<NCA> NAX::AsNCA() const { |
| 142 | if (type == NAXContentType::NCA) | 141 | if (type == NAXContentType::NCA) |
| 143 | return std::make_shared<NCA>(GetDecrypted()); | 142 | return std::make_unique<NCA>(GetDecrypted()); |
| 144 | return nullptr; | 143 | return nullptr; |
| 145 | } | 144 | } |
| 146 | 145 | ||
diff --git a/src/core/file_sys/xts_archive.h b/src/core/file_sys/xts_archive.h index 55d2154a6..8fedd8585 100644 --- a/src/core/file_sys/xts_archive.h +++ b/src/core/file_sys/xts_archive.h | |||
| @@ -33,12 +33,13 @@ class NAX : public ReadOnlyVfsDirectory { | |||
| 33 | public: | 33 | public: |
| 34 | explicit NAX(VirtualFile file); | 34 | explicit NAX(VirtualFile file); |
| 35 | explicit NAX(VirtualFile file, std::array<u8, 0x10> nca_id); | 35 | explicit NAX(VirtualFile file, std::array<u8, 0x10> nca_id); |
| 36 | ~NAX() override; | ||
| 36 | 37 | ||
| 37 | Loader::ResultStatus GetStatus() const; | 38 | Loader::ResultStatus GetStatus() const; |
| 38 | 39 | ||
| 39 | VirtualFile GetDecrypted() const; | 40 | VirtualFile GetDecrypted() const; |
| 40 | 41 | ||
| 41 | std::shared_ptr<NCA> AsNCA() const; | 42 | std::unique_ptr<NCA> AsNCA() const; |
| 42 | 43 | ||
| 43 | NAXContentType GetContentType() const; | 44 | NAXContentType GetContentType() const; |
| 44 | 45 | ||
| @@ -60,7 +61,7 @@ private: | |||
| 60 | 61 | ||
| 61 | VirtualFile file; | 62 | VirtualFile file; |
| 62 | Loader::ResultStatus status; | 63 | Loader::ResultStatus status; |
| 63 | NAXContentType type; | 64 | NAXContentType type{}; |
| 64 | 65 | ||
| 65 | VirtualFile dec_file; | 66 | VirtualFile dec_file; |
| 66 | 67 | ||
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index 332e5c3d0..5bc947010 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp | |||
| @@ -37,7 +37,9 @@ | |||
| 37 | #include "core/core.h" | 37 | #include "core/core.h" |
| 38 | #include "core/core_cpu.h" | 38 | #include "core/core_cpu.h" |
| 39 | #include "core/gdbstub/gdbstub.h" | 39 | #include "core/gdbstub/gdbstub.h" |
| 40 | #include "core/hle/kernel/process.h" | ||
| 40 | #include "core/hle/kernel/scheduler.h" | 41 | #include "core/hle/kernel/scheduler.h" |
| 42 | #include "core/hle/kernel/vm_manager.h" | ||
| 41 | #include "core/loader/loader.h" | 43 | #include "core/loader/loader.h" |
| 42 | #include "core/memory.h" | 44 | #include "core/memory.h" |
| 43 | 45 | ||
| @@ -65,9 +67,9 @@ constexpr u32 MSG_WAITALL = 8; | |||
| 65 | constexpr u32 LR_REGISTER = 30; | 67 | constexpr u32 LR_REGISTER = 30; |
| 66 | constexpr u32 SP_REGISTER = 31; | 68 | constexpr u32 SP_REGISTER = 31; |
| 67 | constexpr u32 PC_REGISTER = 32; | 69 | constexpr u32 PC_REGISTER = 32; |
| 68 | constexpr u32 CPSR_REGISTER = 33; | 70 | constexpr u32 PSTATE_REGISTER = 33; |
| 69 | constexpr u32 UC_ARM64_REG_Q0 = 34; | 71 | constexpr u32 UC_ARM64_REG_Q0 = 34; |
| 70 | constexpr u32 FPSCR_REGISTER = 66; | 72 | constexpr u32 FPCR_REGISTER = 66; |
| 71 | 73 | ||
| 72 | // TODO/WiP - Used while working on support for FPU | 74 | // TODO/WiP - Used while working on support for FPU |
| 73 | constexpr u32 TODO_DUMMY_REG_997 = 997; | 75 | constexpr u32 TODO_DUMMY_REG_997 = 997; |
| @@ -116,7 +118,7 @@ constexpr char target_xml[] = | |||
| 116 | 118 | ||
| 117 | <reg name="pc" bitsize="64" type="code_ptr"/> | 119 | <reg name="pc" bitsize="64" type="code_ptr"/> |
| 118 | 120 | ||
| 119 | <flags id="cpsr_flags" size="4"> | 121 | <flags id="pstate_flags" size="4"> |
| 120 | <field name="SP" start="0" end="0"/> | 122 | <field name="SP" start="0" end="0"/> |
| 121 | <field name="" start="1" end="1"/> | 123 | <field name="" start="1" end="1"/> |
| 122 | <field name="EL" start="2" end="3"/> | 124 | <field name="EL" start="2" end="3"/> |
| @@ -135,7 +137,7 @@ constexpr char target_xml[] = | |||
| 135 | <field name="Z" start="30" end="30"/> | 137 | <field name="Z" start="30" end="30"/> |
| 136 | <field name="N" start="31" end="31"/> | 138 | <field name="N" start="31" end="31"/> |
| 137 | </flags> | 139 | </flags> |
| 138 | <reg name="cpsr" bitsize="32" type="cpsr_flags"/> | 140 | <reg name="pstate" bitsize="32" type="pstate_flags"/> |
| 139 | </feature> | 141 | </feature> |
| 140 | <feature name="org.gnu.gdb.aarch64.fpu"> | 142 | <feature name="org.gnu.gdb.aarch64.fpu"> |
| 141 | </feature> | 143 | </feature> |
| @@ -227,10 +229,10 @@ static u64 RegRead(std::size_t id, Kernel::Thread* thread = nullptr) { | |||
| 227 | return thread->context.sp; | 229 | return thread->context.sp; |
| 228 | } else if (id == PC_REGISTER) { | 230 | } else if (id == PC_REGISTER) { |
| 229 | return thread->context.pc; | 231 | return thread->context.pc; |
| 230 | } else if (id == CPSR_REGISTER) { | 232 | } else if (id == PSTATE_REGISTER) { |
| 231 | return thread->context.cpsr; | 233 | return thread->context.pstate; |
| 232 | } else if (id > CPSR_REGISTER && id < FPSCR_REGISTER) { | 234 | } else if (id > PSTATE_REGISTER && id < FPCR_REGISTER) { |
| 233 | return thread->context.fpu_registers[id - UC_ARM64_REG_Q0][0]; | 235 | return thread->context.vector_registers[id - UC_ARM64_REG_Q0][0]; |
| 234 | } else { | 236 | } else { |
| 235 | return 0; | 237 | return 0; |
| 236 | } | 238 | } |
| @@ -247,10 +249,10 @@ static void RegWrite(std::size_t id, u64 val, Kernel::Thread* thread = nullptr) | |||
| 247 | thread->context.sp = val; | 249 | thread->context.sp = val; |
| 248 | } else if (id == PC_REGISTER) { | 250 | } else if (id == PC_REGISTER) { |
| 249 | thread->context.pc = val; | 251 | thread->context.pc = val; |
| 250 | } else if (id == CPSR_REGISTER) { | 252 | } else if (id == PSTATE_REGISTER) { |
| 251 | thread->context.cpsr = val; | 253 | thread->context.pstate = static_cast<u32>(val); |
| 252 | } else if (id > CPSR_REGISTER && id < FPSCR_REGISTER) { | 254 | } else if (id > PSTATE_REGISTER && id < FPCR_REGISTER) { |
| 253 | thread->context.fpu_registers[id - (CPSR_REGISTER + 1)][0] = val; | 255 | thread->context.vector_registers[id - (PSTATE_REGISTER + 1)][0] = val; |
| 254 | } | 256 | } |
| 255 | } | 257 | } |
| 256 | 258 | ||
| @@ -292,7 +294,7 @@ static u8 NibbleToHex(u8 n) { | |||
| 292 | * @param src Pointer to array of output hex string characters. | 294 | * @param src Pointer to array of output hex string characters. |
| 293 | * @param len Length of src array. | 295 | * @param len Length of src array. |
| 294 | */ | 296 | */ |
| 295 | static u32 HexToInt(const u8* src, size_t len) { | 297 | static u32 HexToInt(const u8* src, std::size_t len) { |
| 296 | u32 output = 0; | 298 | u32 output = 0; |
| 297 | while (len-- > 0) { | 299 | while (len-- > 0) { |
| 298 | output = (output << 4) | HexCharToValue(src[0]); | 300 | output = (output << 4) | HexCharToValue(src[0]); |
| @@ -307,7 +309,7 @@ static u32 HexToInt(const u8* src, size_t len) { | |||
| 307 | * @param src Pointer to array of output hex string characters. | 309 | * @param src Pointer to array of output hex string characters. |
| 308 | * @param len Length of src array. | 310 | * @param len Length of src array. |
| 309 | */ | 311 | */ |
| 310 | static u64 HexToLong(const u8* src, size_t len) { | 312 | static u64 HexToLong(const u8* src, std::size_t len) { |
| 311 | u64 output = 0; | 313 | u64 output = 0; |
| 312 | while (len-- > 0) { | 314 | while (len-- > 0) { |
| 313 | output = (output << 4) | HexCharToValue(src[0]); | 315 | output = (output << 4) | HexCharToValue(src[0]); |
| @@ -323,7 +325,7 @@ static u64 HexToLong(const u8* src, size_t len) { | |||
| 323 | * @param src Pointer to array of u8 bytes. | 325 | * @param src Pointer to array of u8 bytes. |
| 324 | * @param len Length of src array. | 326 | * @param len Length of src array. |
| 325 | */ | 327 | */ |
| 326 | static void MemToGdbHex(u8* dest, const u8* src, size_t len) { | 328 | static void MemToGdbHex(u8* dest, const u8* src, std::size_t len) { |
| 327 | while (len-- > 0) { | 329 | while (len-- > 0) { |
| 328 | u8 tmp = *src++; | 330 | u8 tmp = *src++; |
| 329 | *dest++ = NibbleToHex(tmp >> 4); | 331 | *dest++ = NibbleToHex(tmp >> 4); |
| @@ -338,7 +340,7 @@ static void MemToGdbHex(u8* dest, const u8* src, size_t len) { | |||
| 338 | * @param src Pointer to array of output hex string characters. | 340 | * @param src Pointer to array of output hex string characters. |
| 339 | * @param len Length of src array. | 341 | * @param len Length of src array. |
| 340 | */ | 342 | */ |
| 341 | static void GdbHexToMem(u8* dest, const u8* src, size_t len) { | 343 | static void GdbHexToMem(u8* dest, const u8* src, std::size_t len) { |
| 342 | while (len-- > 0) { | 344 | while (len-- > 0) { |
| 343 | *dest++ = (HexCharToValue(src[0]) << 4) | HexCharToValue(src[1]); | 345 | *dest++ = (HexCharToValue(src[0]) << 4) | HexCharToValue(src[1]); |
| 344 | src += 2; | 346 | src += 2; |
| @@ -406,7 +408,7 @@ static u64 GdbHexToLong(const u8* src) { | |||
| 406 | /// Read a byte from the gdb client. | 408 | /// Read a byte from the gdb client. |
| 407 | static u8 ReadByte() { | 409 | static u8 ReadByte() { |
| 408 | u8 c; | 410 | u8 c; |
| 409 | size_t received_size = recv(gdbserver_socket, reinterpret_cast<char*>(&c), 1, MSG_WAITALL); | 411 | std::size_t received_size = recv(gdbserver_socket, reinterpret_cast<char*>(&c), 1, MSG_WAITALL); |
| 410 | if (received_size != 1) { | 412 | if (received_size != 1) { |
| 411 | LOG_ERROR(Debug_GDBStub, "recv failed: {}", received_size); | 413 | LOG_ERROR(Debug_GDBStub, "recv failed: {}", received_size); |
| 412 | Shutdown(); | 414 | Shutdown(); |
| @@ -416,7 +418,7 @@ static u8 ReadByte() { | |||
| 416 | } | 418 | } |
| 417 | 419 | ||
| 418 | /// Calculate the checksum of the current command buffer. | 420 | /// Calculate the checksum of the current command buffer. |
| 419 | static u8 CalculateChecksum(const u8* buffer, size_t length) { | 421 | static u8 CalculateChecksum(const u8* buffer, std::size_t length) { |
| 420 | return static_cast<u8>(std::accumulate(buffer, buffer + length, 0, std::plus<u8>())); | 422 | return static_cast<u8>(std::accumulate(buffer, buffer + length, 0, std::plus<u8>())); |
| 421 | } | 423 | } |
| 422 | 424 | ||
| @@ -518,7 +520,7 @@ bool CheckBreakpoint(VAddr addr, BreakpointType type) { | |||
| 518 | * @param packet Packet to be sent to client. | 520 | * @param packet Packet to be sent to client. |
| 519 | */ | 521 | */ |
| 520 | static void SendPacket(const char packet) { | 522 | static void SendPacket(const char packet) { |
| 521 | size_t sent_size = send(gdbserver_socket, &packet, 1, 0); | 523 | std::size_t sent_size = send(gdbserver_socket, &packet, 1, 0); |
| 522 | if (sent_size != 1) { | 524 | if (sent_size != 1) { |
| 523 | LOG_ERROR(Debug_GDBStub, "send failed"); | 525 | LOG_ERROR(Debug_GDBStub, "send failed"); |
| 524 | } | 526 | } |
| @@ -585,7 +587,8 @@ static void HandleQuery() { | |||
| 585 | strlen("Xfer:features:read:target.xml:")) == 0) { | 587 | strlen("Xfer:features:read:target.xml:")) == 0) { |
| 586 | SendReply(target_xml); | 588 | SendReply(target_xml); |
| 587 | } else if (strncmp(query, "Offsets", strlen("Offsets")) == 0) { | 589 | } else if (strncmp(query, "Offsets", strlen("Offsets")) == 0) { |
| 588 | std::string buffer = fmt::format("TextSeg={:0x}", Memory::PROCESS_IMAGE_VADDR); | 590 | const VAddr base_address = Core::CurrentProcess()->VMManager().GetCodeRegionBaseAddress(); |
| 591 | std::string buffer = fmt::format("TextSeg={:0x}", base_address); | ||
| 589 | SendReply(buffer.c_str()); | 592 | SendReply(buffer.c_str()); |
| 590 | } else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) { | 593 | } else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) { |
| 591 | std::string val = "m"; | 594 | std::string val = "m"; |
| @@ -781,11 +784,11 @@ static void ReadRegister() { | |||
| 781 | LongToGdbHex(reply, RegRead(id, current_thread)); | 784 | LongToGdbHex(reply, RegRead(id, current_thread)); |
| 782 | } else if (id == PC_REGISTER) { | 785 | } else if (id == PC_REGISTER) { |
| 783 | LongToGdbHex(reply, RegRead(id, current_thread)); | 786 | LongToGdbHex(reply, RegRead(id, current_thread)); |
| 784 | } else if (id == CPSR_REGISTER) { | 787 | } else if (id == PSTATE_REGISTER) { |
| 785 | IntToGdbHex(reply, (u32)RegRead(id, current_thread)); | 788 | IntToGdbHex(reply, static_cast<u32>(RegRead(id, current_thread))); |
| 786 | } else if (id >= UC_ARM64_REG_Q0 && id < FPSCR_REGISTER) { | 789 | } else if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) { |
| 787 | LongToGdbHex(reply, RegRead(id, current_thread)); | 790 | LongToGdbHex(reply, RegRead(id, current_thread)); |
| 788 | } else if (id == FPSCR_REGISTER) { | 791 | } else if (id == FPCR_REGISTER) { |
| 789 | LongToGdbHex(reply, RegRead(TODO_DUMMY_REG_998, current_thread)); | 792 | LongToGdbHex(reply, RegRead(TODO_DUMMY_REG_998, current_thread)); |
| 790 | } else { | 793 | } else { |
| 791 | LongToGdbHex(reply, RegRead(TODO_DUMMY_REG_997, current_thread)); | 794 | LongToGdbHex(reply, RegRead(TODO_DUMMY_REG_997, current_thread)); |
| @@ -811,7 +814,7 @@ static void ReadRegisters() { | |||
| 811 | 814 | ||
| 812 | bufptr += 16; | 815 | bufptr += 16; |
| 813 | 816 | ||
| 814 | IntToGdbHex(bufptr, (u32)RegRead(CPSR_REGISTER, current_thread)); | 817 | IntToGdbHex(bufptr, static_cast<u32>(RegRead(PSTATE_REGISTER, current_thread))); |
| 815 | 818 | ||
| 816 | bufptr += 8; | 819 | bufptr += 8; |
| 817 | 820 | ||
| @@ -843,11 +846,11 @@ static void WriteRegister() { | |||
| 843 | RegWrite(id, GdbHexToLong(buffer_ptr), current_thread); | 846 | RegWrite(id, GdbHexToLong(buffer_ptr), current_thread); |
| 844 | } else if (id == PC_REGISTER) { | 847 | } else if (id == PC_REGISTER) { |
| 845 | RegWrite(id, GdbHexToLong(buffer_ptr), current_thread); | 848 | RegWrite(id, GdbHexToLong(buffer_ptr), current_thread); |
| 846 | } else if (id == CPSR_REGISTER) { | 849 | } else if (id == PSTATE_REGISTER) { |
| 847 | RegWrite(id, GdbHexToInt(buffer_ptr), current_thread); | 850 | RegWrite(id, GdbHexToInt(buffer_ptr), current_thread); |
| 848 | } else if (id >= UC_ARM64_REG_Q0 && id < FPSCR_REGISTER) { | 851 | } else if (id >= UC_ARM64_REG_Q0 && id < FPCR_REGISTER) { |
| 849 | RegWrite(id, GdbHexToLong(buffer_ptr), current_thread); | 852 | RegWrite(id, GdbHexToLong(buffer_ptr), current_thread); |
| 850 | } else if (id == FPSCR_REGISTER) { | 853 | } else if (id == FPCR_REGISTER) { |
| 851 | RegWrite(TODO_DUMMY_REG_998, GdbHexToLong(buffer_ptr), current_thread); | 854 | RegWrite(TODO_DUMMY_REG_998, GdbHexToLong(buffer_ptr), current_thread); |
| 852 | } else { | 855 | } else { |
| 853 | RegWrite(TODO_DUMMY_REG_997, GdbHexToLong(buffer_ptr), current_thread); | 856 | RegWrite(TODO_DUMMY_REG_997, GdbHexToLong(buffer_ptr), current_thread); |
| @@ -866,16 +869,16 @@ static void WriteRegisters() { | |||
| 866 | if (command_buffer[0] != 'G') | 869 | if (command_buffer[0] != 'G') |
| 867 | return SendReply("E01"); | 870 | return SendReply("E01"); |
| 868 | 871 | ||
| 869 | for (u32 i = 0, reg = 0; reg <= FPSCR_REGISTER; i++, reg++) { | 872 | for (u32 i = 0, reg = 0; reg <= FPCR_REGISTER; i++, reg++) { |
| 870 | if (reg <= SP_REGISTER) { | 873 | if (reg <= SP_REGISTER) { |
| 871 | RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread); | 874 | RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread); |
| 872 | } else if (reg == PC_REGISTER) { | 875 | } else if (reg == PC_REGISTER) { |
| 873 | RegWrite(PC_REGISTER, GdbHexToLong(buffer_ptr + i * 16), current_thread); | 876 | RegWrite(PC_REGISTER, GdbHexToLong(buffer_ptr + i * 16), current_thread); |
| 874 | } else if (reg == CPSR_REGISTER) { | 877 | } else if (reg == PSTATE_REGISTER) { |
| 875 | RegWrite(CPSR_REGISTER, GdbHexToInt(buffer_ptr + i * 16), current_thread); | 878 | RegWrite(PSTATE_REGISTER, GdbHexToInt(buffer_ptr + i * 16), current_thread); |
| 876 | } else if (reg >= UC_ARM64_REG_Q0 && reg < FPSCR_REGISTER) { | 879 | } else if (reg >= UC_ARM64_REG_Q0 && reg < FPCR_REGISTER) { |
| 877 | RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread); | 880 | RegWrite(reg, GdbHexToLong(buffer_ptr + i * 16), current_thread); |
| 878 | } else if (reg == FPSCR_REGISTER) { | 881 | } else if (reg == FPCR_REGISTER) { |
| 879 | RegWrite(TODO_DUMMY_REG_998, GdbHexToLong(buffer_ptr + i * 16), current_thread); | 882 | RegWrite(TODO_DUMMY_REG_998, GdbHexToLong(buffer_ptr + i * 16), current_thread); |
| 880 | } else { | 883 | } else { |
| 881 | UNIMPLEMENTED(); | 884 | UNIMPLEMENTED(); |
| @@ -893,11 +896,11 @@ static void ReadMemory() { | |||
| 893 | static u8 reply[GDB_BUFFER_SIZE - 4]; | 896 | static u8 reply[GDB_BUFFER_SIZE - 4]; |
| 894 | 897 | ||
| 895 | auto start_offset = command_buffer + 1; | 898 | auto start_offset = command_buffer + 1; |
| 896 | auto addr_pos = std::find(start_offset, command_buffer + command_length, ','); | 899 | const auto addr_pos = std::find(start_offset, command_buffer + command_length, ','); |
| 897 | VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset)); | 900 | const VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset)); |
| 898 | 901 | ||
| 899 | start_offset = addr_pos + 1; | 902 | start_offset = addr_pos + 1; |
| 900 | u64 len = | 903 | const u64 len = |
| 901 | HexToLong(start_offset, static_cast<u64>((command_buffer + command_length) - start_offset)); | 904 | HexToLong(start_offset, static_cast<u64>((command_buffer + command_length) - start_offset)); |
| 902 | 905 | ||
| 903 | LOG_DEBUG(Debug_GDBStub, "gdb: addr: {:016X} len: {:016X}", addr, len); | 906 | LOG_DEBUG(Debug_GDBStub, "gdb: addr: {:016X} len: {:016X}", addr, len); |
| @@ -906,7 +909,9 @@ static void ReadMemory() { | |||
| 906 | SendReply("E01"); | 909 | SendReply("E01"); |
| 907 | } | 910 | } |
| 908 | 911 | ||
| 909 | if (addr < Memory::PROCESS_IMAGE_VADDR || addr >= Memory::MAP_REGION_VADDR_END) { | 912 | const auto& vm_manager = Core::CurrentProcess()->VMManager(); |
| 913 | if (addr < vm_manager.GetCodeRegionBaseAddress() || | ||
| 914 | addr >= vm_manager.GetMapRegionEndAddress()) { | ||
| 910 | return SendReply("E00"); | 915 | return SendReply("E00"); |
| 911 | } | 916 | } |
| 912 | 917 | ||
| @@ -995,7 +1000,7 @@ static bool CommitBreakpoint(BreakpointType type, VAddr addr, u64 len) { | |||
| 995 | breakpoint.addr = addr; | 1000 | breakpoint.addr = addr; |
| 996 | breakpoint.len = len; | 1001 | breakpoint.len = len; |
| 997 | Memory::ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size()); | 1002 | Memory::ReadBlock(addr, breakpoint.inst.data(), breakpoint.inst.size()); |
| 998 | static constexpr std::array<u8, 4> btrap{{0xd4, 0x20, 0x7d, 0x0}}; | 1003 | static constexpr std::array<u8, 4> btrap{{0x00, 0x7d, 0x20, 0xd4}}; |
| 999 | Memory::WriteBlock(addr, btrap.data(), btrap.size()); | 1004 | Memory::WriteBlock(addr, btrap.data(), btrap.size()); |
| 1000 | Core::System::GetInstance().InvalidateCpuInstructionCaches(); | 1005 | Core::System::GetInstance().InvalidateCpuInstructionCaches(); |
| 1001 | p.insert({addr, breakpoint}); | 1006 | p.insert({addr, breakpoint}); |
diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h index 545cd884a..419f45896 100644 --- a/src/core/hle/ipc.h +++ b/src/core/hle/ipc.h | |||
| @@ -12,7 +12,7 @@ | |||
| 12 | namespace IPC { | 12 | namespace IPC { |
| 13 | 13 | ||
| 14 | /// Size of the command buffer area, in 32-bit words. | 14 | /// Size of the command buffer area, in 32-bit words. |
| 15 | constexpr size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32); | 15 | constexpr std::size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32); |
| 16 | 16 | ||
| 17 | // These errors are commonly returned by invalid IPC translations, so alias them here for | 17 | // These errors are commonly returned by invalid IPC translations, so alias them here for |
| 18 | // convenience. | 18 | // convenience. |
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index 0f3ffdb60..a4bfe2eb0 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h | |||
| @@ -152,8 +152,8 @@ public: | |||
| 152 | } | 152 | } |
| 153 | 153 | ||
| 154 | void ValidateHeader() { | 154 | void ValidateHeader() { |
| 155 | const size_t num_domain_objects = context->NumDomainObjects(); | 155 | const std::size_t num_domain_objects = context->NumDomainObjects(); |
| 156 | const size_t num_move_objects = context->NumMoveObjects(); | 156 | const std::size_t num_move_objects = context->NumMoveObjects(); |
| 157 | ASSERT_MSG(!num_domain_objects || !num_move_objects, | 157 | ASSERT_MSG(!num_domain_objects || !num_move_objects, |
| 158 | "cannot move normal handles and domain objects"); | 158 | "cannot move normal handles and domain objects"); |
| 159 | ASSERT_MSG((index - datapayload_index) == normal_params_size, | 159 | ASSERT_MSG((index - datapayload_index) == normal_params_size, |
| @@ -290,13 +290,6 @@ public: | |||
| 290 | Skip(CommandIdSize, false); | 290 | Skip(CommandIdSize, false); |
| 291 | } | 291 | } |
| 292 | 292 | ||
| 293 | ResponseBuilder MakeBuilder(u32 normal_params_size, u32 num_handles_to_copy, | ||
| 294 | u32 num_handles_to_move, | ||
| 295 | ResponseBuilder::Flags flags = ResponseBuilder::Flags::None) const { | ||
| 296 | return ResponseBuilder{*context, normal_params_size, num_handles_to_copy, | ||
| 297 | num_handles_to_move, flags}; | ||
| 298 | } | ||
| 299 | |||
| 300 | template <typename T> | 293 | template <typename T> |
| 301 | T Pop(); | 294 | T Pop(); |
| 302 | 295 | ||
| @@ -329,10 +322,10 @@ public: | |||
| 329 | T PopRaw(); | 322 | T PopRaw(); |
| 330 | 323 | ||
| 331 | template <typename T> | 324 | template <typename T> |
| 332 | Kernel::SharedPtr<T> GetMoveObject(size_t index); | 325 | Kernel::SharedPtr<T> GetMoveObject(std::size_t index); |
| 333 | 326 | ||
| 334 | template <typename T> | 327 | template <typename T> |
| 335 | Kernel::SharedPtr<T> GetCopyObject(size_t index); | 328 | Kernel::SharedPtr<T> GetCopyObject(std::size_t index); |
| 336 | 329 | ||
| 337 | template <class T> | 330 | template <class T> |
| 338 | std::shared_ptr<T> PopIpcInterface() { | 331 | std::shared_ptr<T> PopIpcInterface() { |
| @@ -406,12 +399,12 @@ void RequestParser::Pop(First& first_value, Other&... other_values) { | |||
| 406 | } | 399 | } |
| 407 | 400 | ||
| 408 | template <typename T> | 401 | template <typename T> |
| 409 | Kernel::SharedPtr<T> RequestParser::GetMoveObject(size_t index) { | 402 | Kernel::SharedPtr<T> RequestParser::GetMoveObject(std::size_t index) { |
| 410 | return context->GetMoveObject<T>(index); | 403 | return context->GetMoveObject<T>(index); |
| 411 | } | 404 | } |
| 412 | 405 | ||
| 413 | template <typename T> | 406 | template <typename T> |
| 414 | Kernel::SharedPtr<T> RequestParser::GetCopyObject(size_t index) { | 407 | Kernel::SharedPtr<T> RequestParser::GetCopyObject(std::size_t index) { |
| 415 | return context->GetCopyObject<T>(index); | 408 | return context->GetCopyObject<T>(index); |
| 416 | } | 409 | } |
| 417 | 410 | ||
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index 6657accd5..93577591f 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp | |||
| @@ -35,16 +35,17 @@ static ResultCode WaitForAddress(VAddr address, s64 timeout) { | |||
| 35 | 35 | ||
| 36 | // Gets the threads waiting on an address. | 36 | // Gets the threads waiting on an address. |
| 37 | static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address) { | 37 | static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address) { |
| 38 | const auto RetrieveWaitingThreads = | 38 | const auto RetrieveWaitingThreads = [](std::size_t core_index, |
| 39 | [](size_t core_index, std::vector<SharedPtr<Thread>>& waiting_threads, VAddr arb_addr) { | 39 | std::vector<SharedPtr<Thread>>& waiting_threads, |
| 40 | const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); | 40 | VAddr arb_addr) { |
| 41 | auto& thread_list = scheduler->GetThreadList(); | 41 | const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); |
| 42 | 42 | auto& thread_list = scheduler->GetThreadList(); | |
| 43 | for (auto& thread : thread_list) { | 43 | |
| 44 | if (thread->arb_wait_address == arb_addr) | 44 | for (auto& thread : thread_list) { |
| 45 | waiting_threads.push_back(thread); | 45 | if (thread->arb_wait_address == arb_addr) |
| 46 | } | 46 | waiting_threads.push_back(thread); |
| 47 | }; | 47 | } |
| 48 | }; | ||
| 48 | 49 | ||
| 49 | // Retrieve all threads that are waiting for this address. | 50 | // Retrieve all threads that are waiting for this address. |
| 50 | std::vector<SharedPtr<Thread>> threads; | 51 | std::vector<SharedPtr<Thread>> threads; |
| @@ -66,12 +67,12 @@ static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address) | |||
| 66 | static void WakeThreads(std::vector<SharedPtr<Thread>>& waiting_threads, s32 num_to_wake) { | 67 | static void WakeThreads(std::vector<SharedPtr<Thread>>& waiting_threads, s32 num_to_wake) { |
| 67 | // Only process up to 'target' threads, unless 'target' is <= 0, in which case process | 68 | // Only process up to 'target' threads, unless 'target' is <= 0, in which case process |
| 68 | // them all. | 69 | // them all. |
| 69 | size_t last = waiting_threads.size(); | 70 | std::size_t last = waiting_threads.size(); |
| 70 | if (num_to_wake > 0) | 71 | if (num_to_wake > 0) |
| 71 | last = num_to_wake; | 72 | last = num_to_wake; |
| 72 | 73 | ||
| 73 | // Signal the waiting threads. | 74 | // Signal the waiting threads. |
| 74 | for (size_t i = 0; i < last; i++) { | 75 | for (std::size_t i = 0; i < last; i++) { |
| 75 | ASSERT(waiting_threads[i]->status == ThreadStatus::WaitArb); | 76 | ASSERT(waiting_threads[i]->status == ThreadStatus::WaitArb); |
| 76 | waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS); | 77 | waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS); |
| 77 | waiting_threads[i]->arb_wait_address = 0; | 78 | waiting_threads[i]->arb_wait_address = 0; |
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h index ad39c8271..e5fa67ae8 100644 --- a/src/core/hle/kernel/errors.h +++ b/src/core/hle/kernel/errors.h | |||
| @@ -17,6 +17,7 @@ enum { | |||
| 17 | 17 | ||
| 18 | // Confirmed Switch OS error codes | 18 | // Confirmed Switch OS error codes |
| 19 | MaxConnectionsReached = 7, | 19 | MaxConnectionsReached = 7, |
| 20 | InvalidSize = 101, | ||
| 20 | InvalidAddress = 102, | 21 | InvalidAddress = 102, |
| 21 | HandleTableFull = 105, | 22 | HandleTableFull = 105, |
| 22 | InvalidMemoryState = 106, | 23 | InvalidMemoryState = 106, |
| @@ -29,6 +30,8 @@ enum { | |||
| 29 | SynchronizationCanceled = 118, | 30 | SynchronizationCanceled = 118, |
| 30 | TooLarge = 119, | 31 | TooLarge = 119, |
| 31 | InvalidEnumValue = 120, | 32 | InvalidEnumValue = 120, |
| 33 | NoSuchEntry = 121, | ||
| 34 | AlreadyRegistered = 122, | ||
| 32 | InvalidState = 125, | 35 | InvalidState = 125, |
| 33 | ResourceLimitExceeded = 132, | 36 | ResourceLimitExceeded = 132, |
| 34 | }; | 37 | }; |
| @@ -55,6 +58,8 @@ constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel, | |||
| 55 | ErrCodes::InvalidMemoryPermissions); | 58 | ErrCodes::InvalidMemoryPermissions); |
| 56 | constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle); | 59 | constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle); |
| 57 | constexpr ResultCode ERR_INVALID_PROCESSOR_ID(ErrorModule::Kernel, ErrCodes::InvalidProcessorId); | 60 | constexpr ResultCode ERR_INVALID_PROCESSOR_ID(ErrorModule::Kernel, ErrCodes::InvalidProcessorId); |
| 61 | constexpr ResultCode ERR_INVALID_SIZE(ErrorModule::Kernel, ErrCodes::InvalidSize); | ||
| 62 | constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorModule::Kernel, ErrCodes::AlreadyRegistered); | ||
| 58 | constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState); | 63 | constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState); |
| 59 | constexpr ResultCode ERR_INVALID_THREAD_PRIORITY(ErrorModule::Kernel, | 64 | constexpr ResultCode ERR_INVALID_THREAD_PRIORITY(ErrorModule::Kernel, |
| 60 | ErrCodes::InvalidThreadPriority); | 65 | ErrCodes::InvalidThreadPriority); |
| @@ -63,7 +68,7 @@ constexpr ResultCode ERR_INVALID_OBJECT_ADDR(-1); | |||
| 63 | constexpr ResultCode ERR_NOT_AUTHORIZED(-1); | 68 | constexpr ResultCode ERR_NOT_AUTHORIZED(-1); |
| 64 | /// Alternate code returned instead of ERR_INVALID_HANDLE in some code paths. | 69 | /// Alternate code returned instead of ERR_INVALID_HANDLE in some code paths. |
| 65 | constexpr ResultCode ERR_INVALID_HANDLE_OS(-1); | 70 | constexpr ResultCode ERR_INVALID_HANDLE_OS(-1); |
| 66 | constexpr ResultCode ERR_NOT_FOUND(-1); | 71 | constexpr ResultCode ERR_NOT_FOUND(ErrorModule::Kernel, ErrCodes::NoSuchEntry); |
| 67 | constexpr ResultCode RESULT_TIMEOUT(ErrorModule::Kernel, ErrCodes::Timeout); | 72 | constexpr ResultCode RESULT_TIMEOUT(ErrorModule::Kernel, ErrCodes::Timeout); |
| 68 | /// Returned when Accept() is called on a port with no sessions to be accepted. | 73 | /// Returned when Accept() is called on a port with no sessions to be accepted. |
| 69 | constexpr ResultCode ERR_NO_PENDING_SESSIONS(-1); | 74 | constexpr ResultCode ERR_NO_PENDING_SESSIONS(-1); |
diff --git a/src/core/hle/kernel/handle_table.cpp b/src/core/hle/kernel/handle_table.cpp index 3a079b9a9..5ee5c05e3 100644 --- a/src/core/hle/kernel/handle_table.cpp +++ b/src/core/hle/kernel/handle_table.cpp | |||
| @@ -65,7 +65,7 @@ ResultCode HandleTable::Close(Handle handle) { | |||
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | bool HandleTable::IsValid(Handle handle) const { | 67 | bool HandleTable::IsValid(Handle handle) const { |
| 68 | size_t slot = GetSlot(handle); | 68 | std::size_t slot = GetSlot(handle); |
| 69 | u16 generation = GetGeneration(handle); | 69 | u16 generation = GetGeneration(handle); |
| 70 | 70 | ||
| 71 | return slot < MAX_COUNT && objects[slot] != nullptr && generations[slot] == generation; | 71 | return slot < MAX_COUNT && objects[slot] != nullptr && generations[slot] == generation; |
diff --git a/src/core/hle/kernel/handle_table.h b/src/core/hle/kernel/handle_table.h index cac928adb..9e2f33e8a 100644 --- a/src/core/hle/kernel/handle_table.h +++ b/src/core/hle/kernel/handle_table.h | |||
| @@ -93,7 +93,7 @@ private: | |||
| 93 | * This is the maximum limit of handles allowed per process in CTR-OS. It can be further | 93 | * This is the maximum limit of handles allowed per process in CTR-OS. It can be further |
| 94 | * reduced by ExHeader values, but this is not emulated here. | 94 | * reduced by ExHeader values, but this is not emulated here. |
| 95 | */ | 95 | */ |
| 96 | static const size_t MAX_COUNT = 4096; | 96 | static const std::size_t MAX_COUNT = 4096; |
| 97 | 97 | ||
| 98 | static u16 GetSlot(Handle handle) { | 98 | static u16 GetSlot(Handle handle) { |
| 99 | return handle >> 15; | 99 | return handle >> 15; |
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 7264be906..72fb9d250 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp | |||
| @@ -42,9 +42,9 @@ SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread, | |||
| 42 | Kernel::SharedPtr<Kernel::Event> event) { | 42 | Kernel::SharedPtr<Kernel::Event> event) { |
| 43 | 43 | ||
| 44 | // Put the client thread to sleep until the wait event is signaled or the timeout expires. | 44 | // Put the client thread to sleep until the wait event is signaled or the timeout expires. |
| 45 | thread->wakeup_callback = | 45 | thread->wakeup_callback = [context = *this, callback]( |
| 46 | [context = *this, callback](ThreadWakeupReason reason, SharedPtr<Thread> thread, | 46 | ThreadWakeupReason reason, SharedPtr<Thread> thread, |
| 47 | SharedPtr<WaitObject> object, size_t index) mutable -> bool { | 47 | SharedPtr<WaitObject> object, std::size_t index) mutable -> bool { |
| 48 | ASSERT(thread->status == ThreadStatus::WaitHLEEvent); | 48 | ASSERT(thread->status == ThreadStatus::WaitHLEEvent); |
| 49 | callback(thread, context, reason); | 49 | callback(thread, context, reason); |
| 50 | context.WriteToOutgoingCommandBuffer(*thread); | 50 | context.WriteToOutgoingCommandBuffer(*thread); |
| @@ -199,8 +199,8 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdb | |||
| 199 | } | 199 | } |
| 200 | 200 | ||
| 201 | // The data_size already includes the payload header, the padding and the domain header. | 201 | // The data_size already includes the payload header, the padding and the domain header. |
| 202 | size_t size = data_payload_offset + command_header->data_size - | 202 | std::size_t size = data_payload_offset + command_header->data_size - |
| 203 | sizeof(IPC::DataPayloadHeader) / sizeof(u32) - 4; | 203 | sizeof(IPC::DataPayloadHeader) / sizeof(u32) - 4; |
| 204 | if (domain_message_header) | 204 | if (domain_message_header) |
| 205 | size -= sizeof(IPC::DomainMessageHeader) / sizeof(u32); | 205 | size -= sizeof(IPC::DomainMessageHeader) / sizeof(u32); |
| 206 | std::copy_n(src_cmdbuf, size, cmd_buf.begin()); | 206 | std::copy_n(src_cmdbuf, size, cmd_buf.begin()); |
| @@ -217,8 +217,8 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread) | |||
| 217 | ParseCommandBuffer(cmd_buf.data(), false); | 217 | ParseCommandBuffer(cmd_buf.data(), false); |
| 218 | 218 | ||
| 219 | // The data_size already includes the payload header, the padding and the domain header. | 219 | // The data_size already includes the payload header, the padding and the domain header. |
| 220 | size_t size = data_payload_offset + command_header->data_size - | 220 | std::size_t size = data_payload_offset + command_header->data_size - |
| 221 | sizeof(IPC::DataPayloadHeader) / sizeof(u32) - 4; | 221 | sizeof(IPC::DataPayloadHeader) / sizeof(u32) - 4; |
| 222 | if (domain_message_header) | 222 | if (domain_message_header) |
| 223 | size -= sizeof(IPC::DomainMessageHeader) / sizeof(u32); | 223 | size -= sizeof(IPC::DomainMessageHeader) / sizeof(u32); |
| 224 | 224 | ||
| @@ -229,7 +229,7 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread) | |||
| 229 | "Handle descriptor bit set but no handles to translate"); | 229 | "Handle descriptor bit set but no handles to translate"); |
| 230 | // We write the translated handles at a specific offset in the command buffer, this space | 230 | // We write the translated handles at a specific offset in the command buffer, this space |
| 231 | // was already reserved when writing the header. | 231 | // was already reserved when writing the header. |
| 232 | size_t current_offset = | 232 | std::size_t current_offset = |
| 233 | (sizeof(IPC::CommandHeader) + sizeof(IPC::HandleDescriptorHeader)) / sizeof(u32); | 233 | (sizeof(IPC::CommandHeader) + sizeof(IPC::HandleDescriptorHeader)) / sizeof(u32); |
| 234 | ASSERT_MSG(!handle_descriptor_header->send_current_pid, "Sending PID is not implemented"); | 234 | ASSERT_MSG(!handle_descriptor_header->send_current_pid, "Sending PID is not implemented"); |
| 235 | 235 | ||
| @@ -258,7 +258,7 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread) | |||
| 258 | ASSERT(domain_message_header->num_objects == domain_objects.size()); | 258 | ASSERT(domain_message_header->num_objects == domain_objects.size()); |
| 259 | // Write the domain objects to the command buffer, these go after the raw untranslated data. | 259 | // Write the domain objects to the command buffer, these go after the raw untranslated data. |
| 260 | // TODO(Subv): This completely ignores C buffers. | 260 | // TODO(Subv): This completely ignores C buffers. |
| 261 | size_t domain_offset = size - domain_message_header->num_objects; | 261 | std::size_t domain_offset = size - domain_message_header->num_objects; |
| 262 | auto& request_handlers = server_session->domain_request_handlers; | 262 | auto& request_handlers = server_session->domain_request_handlers; |
| 263 | 263 | ||
| 264 | for (auto& object : domain_objects) { | 264 | for (auto& object : domain_objects) { |
| @@ -291,14 +291,15 @@ std::vector<u8> HLERequestContext::ReadBuffer(int buffer_index) const { | |||
| 291 | return buffer; | 291 | return buffer; |
| 292 | } | 292 | } |
| 293 | 293 | ||
| 294 | size_t HLERequestContext::WriteBuffer(const void* buffer, size_t size, int buffer_index) const { | 294 | std::size_t HLERequestContext::WriteBuffer(const void* buffer, std::size_t size, |
| 295 | int buffer_index) const { | ||
| 295 | if (size == 0) { | 296 | if (size == 0) { |
| 296 | LOG_WARNING(Core, "skip empty buffer write"); | 297 | LOG_WARNING(Core, "skip empty buffer write"); |
| 297 | return 0; | 298 | return 0; |
| 298 | } | 299 | } |
| 299 | 300 | ||
| 300 | const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[buffer_index].Size()}; | 301 | const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[buffer_index].Size()}; |
| 301 | const size_t buffer_size{GetWriteBufferSize(buffer_index)}; | 302 | const std::size_t buffer_size{GetWriteBufferSize(buffer_index)}; |
| 302 | if (size > buffer_size) { | 303 | if (size > buffer_size) { |
| 303 | LOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size, | 304 | LOG_CRITICAL(Core, "size ({:016X}) is greater than buffer_size ({:016X})", size, |
| 304 | buffer_size); | 305 | buffer_size); |
| @@ -314,13 +315,13 @@ size_t HLERequestContext::WriteBuffer(const void* buffer, size_t size, int buffe | |||
| 314 | return size; | 315 | return size; |
| 315 | } | 316 | } |
| 316 | 317 | ||
| 317 | size_t HLERequestContext::GetReadBufferSize(int buffer_index) const { | 318 | std::size_t HLERequestContext::GetReadBufferSize(int buffer_index) const { |
| 318 | const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[buffer_index].Size()}; | 319 | const bool is_buffer_a{BufferDescriptorA().size() && BufferDescriptorA()[buffer_index].Size()}; |
| 319 | return is_buffer_a ? BufferDescriptorA()[buffer_index].Size() | 320 | return is_buffer_a ? BufferDescriptorA()[buffer_index].Size() |
| 320 | : BufferDescriptorX()[buffer_index].Size(); | 321 | : BufferDescriptorX()[buffer_index].Size(); |
| 321 | } | 322 | } |
| 322 | 323 | ||
| 323 | size_t HLERequestContext::GetWriteBufferSize(int buffer_index) const { | 324 | std::size_t HLERequestContext::GetWriteBufferSize(int buffer_index) const { |
| 324 | const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[buffer_index].Size()}; | 325 | const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[buffer_index].Size()}; |
| 325 | return is_buffer_b ? BufferDescriptorB()[buffer_index].Size() | 326 | return is_buffer_b ? BufferDescriptorB()[buffer_index].Size() |
| 326 | : BufferDescriptorC()[buffer_index].Size(); | 327 | : BufferDescriptorC()[buffer_index].Size(); |
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index f0d07f1b6..894479ee0 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h | |||
| @@ -170,7 +170,7 @@ public: | |||
| 170 | std::vector<u8> ReadBuffer(int buffer_index = 0) const; | 170 | std::vector<u8> ReadBuffer(int buffer_index = 0) const; |
| 171 | 171 | ||
| 172 | /// Helper function to write a buffer using the appropriate buffer descriptor | 172 | /// Helper function to write a buffer using the appropriate buffer descriptor |
| 173 | size_t WriteBuffer(const void* buffer, size_t size, int buffer_index = 0) const; | 173 | std::size_t WriteBuffer(const void* buffer, std::size_t size, int buffer_index = 0) const; |
| 174 | 174 | ||
| 175 | /* Helper function to write a buffer using the appropriate buffer descriptor | 175 | /* Helper function to write a buffer using the appropriate buffer descriptor |
| 176 | * | 176 | * |
| @@ -182,7 +182,7 @@ public: | |||
| 182 | */ | 182 | */ |
| 183 | template <typename ContiguousContainer, | 183 | template <typename ContiguousContainer, |
| 184 | typename = std::enable_if_t<!std::is_pointer_v<ContiguousContainer>>> | 184 | typename = std::enable_if_t<!std::is_pointer_v<ContiguousContainer>>> |
| 185 | size_t WriteBuffer(const ContiguousContainer& container, int buffer_index = 0) const { | 185 | std::size_t WriteBuffer(const ContiguousContainer& container, int buffer_index = 0) const { |
| 186 | using ContiguousType = typename ContiguousContainer::value_type; | 186 | using ContiguousType = typename ContiguousContainer::value_type; |
| 187 | 187 | ||
| 188 | static_assert(std::is_trivially_copyable_v<ContiguousType>, | 188 | static_assert(std::is_trivially_copyable_v<ContiguousType>, |
| @@ -193,19 +193,19 @@ public: | |||
| 193 | } | 193 | } |
| 194 | 194 | ||
| 195 | /// Helper function to get the size of the input buffer | 195 | /// Helper function to get the size of the input buffer |
| 196 | size_t GetReadBufferSize(int buffer_index = 0) const; | 196 | std::size_t GetReadBufferSize(int buffer_index = 0) const; |
| 197 | 197 | ||
| 198 | /// Helper function to get the size of the output buffer | 198 | /// Helper function to get the size of the output buffer |
| 199 | size_t GetWriteBufferSize(int buffer_index = 0) const; | 199 | std::size_t GetWriteBufferSize(int buffer_index = 0) const; |
| 200 | 200 | ||
| 201 | template <typename T> | 201 | template <typename T> |
| 202 | SharedPtr<T> GetCopyObject(size_t index) { | 202 | SharedPtr<T> GetCopyObject(std::size_t index) { |
| 203 | ASSERT(index < copy_objects.size()); | 203 | ASSERT(index < copy_objects.size()); |
| 204 | return DynamicObjectCast<T>(copy_objects[index]); | 204 | return DynamicObjectCast<T>(copy_objects[index]); |
| 205 | } | 205 | } |
| 206 | 206 | ||
| 207 | template <typename T> | 207 | template <typename T> |
| 208 | SharedPtr<T> GetMoveObject(size_t index) { | 208 | SharedPtr<T> GetMoveObject(std::size_t index) { |
| 209 | ASSERT(index < move_objects.size()); | 209 | ASSERT(index < move_objects.size()); |
| 210 | return DynamicObjectCast<T>(move_objects[index]); | 210 | return DynamicObjectCast<T>(move_objects[index]); |
| 211 | } | 211 | } |
| @@ -223,7 +223,7 @@ public: | |||
| 223 | } | 223 | } |
| 224 | 224 | ||
| 225 | template <typename T> | 225 | template <typename T> |
| 226 | std::shared_ptr<T> GetDomainRequestHandler(size_t index) const { | 226 | std::shared_ptr<T> GetDomainRequestHandler(std::size_t index) const { |
| 227 | return std::static_pointer_cast<T>(domain_request_handlers[index]); | 227 | return std::static_pointer_cast<T>(domain_request_handlers[index]); |
| 228 | } | 228 | } |
| 229 | 229 | ||
| @@ -240,15 +240,15 @@ public: | |||
| 240 | domain_objects.clear(); | 240 | domain_objects.clear(); |
| 241 | } | 241 | } |
| 242 | 242 | ||
| 243 | size_t NumMoveObjects() const { | 243 | std::size_t NumMoveObjects() const { |
| 244 | return move_objects.size(); | 244 | return move_objects.size(); |
| 245 | } | 245 | } |
| 246 | 246 | ||
| 247 | size_t NumCopyObjects() const { | 247 | std::size_t NumCopyObjects() const { |
| 248 | return copy_objects.size(); | 248 | return copy_objects.size(); |
| 249 | } | 249 | } |
| 250 | 250 | ||
| 251 | size_t NumDomainObjects() const { | 251 | std::size_t NumDomainObjects() const { |
| 252 | return domain_objects.size(); | 252 | return domain_objects.size(); |
| 253 | } | 253 | } |
| 254 | 254 | ||
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 36bf0b677..81675eac5 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp | |||
| @@ -16,6 +16,7 @@ | |||
| 16 | #include "core/hle/kernel/object.h" | 16 | #include "core/hle/kernel/object.h" |
| 17 | #include "core/hle/kernel/thread.h" | 17 | #include "core/hle/kernel/thread.h" |
| 18 | #include "core/hle/result.h" | 18 | #include "core/hle/result.h" |
| 19 | #include "core/memory.h" | ||
| 19 | 20 | ||
| 20 | namespace Kernel { | 21 | namespace Kernel { |
| 21 | 22 | ||
| @@ -62,7 +63,7 @@ ResultCode Mutex::TryAcquire(HandleTable& handle_table, VAddr address, Handle ho | |||
| 62 | Handle requesting_thread_handle) { | 63 | Handle requesting_thread_handle) { |
| 63 | // The mutex address must be 4-byte aligned | 64 | // The mutex address must be 4-byte aligned |
| 64 | if ((address % sizeof(u32)) != 0) { | 65 | if ((address % sizeof(u32)) != 0) { |
| 65 | return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidAddress); | 66 | return ERR_INVALID_ADDRESS; |
| 66 | } | 67 | } |
| 67 | 68 | ||
| 68 | SharedPtr<Thread> holding_thread = handle_table.Get<Thread>(holding_thread_handle); | 69 | SharedPtr<Thread> holding_thread = handle_table.Get<Thread>(holding_thread_handle); |
| @@ -100,7 +101,7 @@ ResultCode Mutex::TryAcquire(HandleTable& handle_table, VAddr address, Handle ho | |||
| 100 | ResultCode Mutex::Release(VAddr address) { | 101 | ResultCode Mutex::Release(VAddr address) { |
| 101 | // The mutex address must be 4-byte aligned | 102 | // The mutex address must be 4-byte aligned |
| 102 | if ((address % sizeof(u32)) != 0) { | 103 | if ((address % sizeof(u32)) != 0) { |
| 103 | return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidAddress); | 104 | return ERR_INVALID_ADDRESS; |
| 104 | } | 105 | } |
| 105 | 106 | ||
| 106 | auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(GetCurrentThread(), address); | 107 | auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(GetCurrentThread(), address); |
diff --git a/src/core/hle/kernel/object.h b/src/core/hle/kernel/object.h index b054cbf7d..9eb72315c 100644 --- a/src/core/hle/kernel/object.h +++ b/src/core/hle/kernel/object.h | |||
| @@ -6,7 +6,6 @@ | |||
| 6 | 6 | ||
| 7 | #include <atomic> | 7 | #include <atomic> |
| 8 | #include <string> | 8 | #include <string> |
| 9 | #include <utility> | ||
| 10 | 9 | ||
| 11 | #include <boost/smart_ptr/intrusive_ptr.hpp> | 10 | #include <boost/smart_ptr/intrusive_ptr.hpp> |
| 12 | 11 | ||
| @@ -97,7 +96,7 @@ using SharedPtr = boost::intrusive_ptr<T>; | |||
| 97 | template <typename T> | 96 | template <typename T> |
| 98 | inline SharedPtr<T> DynamicObjectCast(SharedPtr<Object> object) { | 97 | inline SharedPtr<T> DynamicObjectCast(SharedPtr<Object> object) { |
| 99 | if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) { | 98 | if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) { |
| 100 | return boost::static_pointer_cast<T>(std::move(object)); | 99 | return boost::static_pointer_cast<T>(object); |
| 101 | } | 100 | } |
| 102 | return nullptr; | 101 | return nullptr; |
| 103 | } | 102 | } |
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index b025e323f..dc9fc8470 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp | |||
| @@ -7,10 +7,13 @@ | |||
| 7 | #include "common/assert.h" | 7 | #include "common/assert.h" |
| 8 | #include "common/common_funcs.h" | 8 | #include "common/common_funcs.h" |
| 9 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 10 | #include "core/core.h" | ||
| 11 | #include "core/file_sys/program_metadata.h" | ||
| 10 | #include "core/hle/kernel/errors.h" | 12 | #include "core/hle/kernel/errors.h" |
| 11 | #include "core/hle/kernel/kernel.h" | 13 | #include "core/hle/kernel/kernel.h" |
| 12 | #include "core/hle/kernel/process.h" | 14 | #include "core/hle/kernel/process.h" |
| 13 | #include "core/hle/kernel/resource_limit.h" | 15 | #include "core/hle/kernel/resource_limit.h" |
| 16 | #include "core/hle/kernel/scheduler.h" | ||
| 14 | #include "core/hle/kernel/thread.h" | 17 | #include "core/hle/kernel/thread.h" |
| 15 | #include "core/hle/kernel/vm_manager.h" | 18 | #include "core/hle/kernel/vm_manager.h" |
| 16 | #include "core/memory.h" | 19 | #include "core/memory.h" |
| @@ -32,16 +35,24 @@ SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) { | |||
| 32 | process->name = std::move(name); | 35 | process->name = std::move(name); |
| 33 | process->flags.raw = 0; | 36 | process->flags.raw = 0; |
| 34 | process->flags.memory_region.Assign(MemoryRegion::APPLICATION); | 37 | process->flags.memory_region.Assign(MemoryRegion::APPLICATION); |
| 38 | process->resource_limit = kernel.ResourceLimitForCategory(ResourceLimitCategory::APPLICATION); | ||
| 35 | process->status = ProcessStatus::Created; | 39 | process->status = ProcessStatus::Created; |
| 36 | process->program_id = 0; | 40 | process->program_id = 0; |
| 37 | process->process_id = kernel.CreateNewProcessID(); | 41 | process->process_id = kernel.CreateNewProcessID(); |
| 42 | process->svc_access_mask.set(); | ||
| 38 | 43 | ||
| 39 | kernel.AppendNewProcess(process); | 44 | kernel.AppendNewProcess(process); |
| 40 | return process; | 45 | return process; |
| 41 | } | 46 | } |
| 42 | 47 | ||
| 43 | void Process::ParseKernelCaps(const u32* kernel_caps, size_t len) { | 48 | void Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) { |
| 44 | for (size_t i = 0; i < len; ++i) { | 49 | program_id = metadata.GetTitleID(); |
| 50 | is_64bit_process = metadata.Is64BitProgram(); | ||
| 51 | vm_manager.Reset(metadata.GetAddressSpaceType()); | ||
| 52 | } | ||
| 53 | |||
| 54 | void Process::ParseKernelCaps(const u32* kernel_caps, std::size_t len) { | ||
| 55 | for (std::size_t i = 0; i < len; ++i) { | ||
| 45 | u32 descriptor = kernel_caps[i]; | 56 | u32 descriptor = kernel_caps[i]; |
| 46 | u32 type = descriptor >> 20; | 57 | u32 type = descriptor >> 20; |
| 47 | 58 | ||
| @@ -117,7 +128,7 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) { | |||
| 117 | // TODO(bunnei): This is heap area that should be allocated by the kernel and not mapped as part | 128 | // TODO(bunnei): This is heap area that should be allocated by the kernel and not mapped as part |
| 118 | // of the user address space. | 129 | // of the user address space. |
| 119 | vm_manager | 130 | vm_manager |
| 120 | .MapMemoryBlock(Memory::STACK_AREA_VADDR_END - stack_size, | 131 | .MapMemoryBlock(vm_manager.GetTLSIORegionEndAddress() - stack_size, |
| 121 | std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size, | 132 | std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size, |
| 122 | MemoryState::Mapped) | 133 | MemoryState::Mapped) |
| 123 | .Unwrap(); | 134 | .Unwrap(); |
| @@ -125,7 +136,92 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) { | |||
| 125 | vm_manager.LogLayout(); | 136 | vm_manager.LogLayout(); |
| 126 | status = ProcessStatus::Running; | 137 | status = ProcessStatus::Running; |
| 127 | 138 | ||
| 128 | Kernel::SetupMainThread(kernel, entry_point, main_thread_priority, this); | 139 | Kernel::SetupMainThread(kernel, entry_point, main_thread_priority, *this); |
| 140 | } | ||
| 141 | |||
| 142 | void Process::PrepareForTermination() { | ||
| 143 | status = ProcessStatus::Exited; | ||
| 144 | |||
| 145 | const auto stop_threads = [this](const std::vector<SharedPtr<Thread>>& thread_list) { | ||
| 146 | for (auto& thread : thread_list) { | ||
| 147 | if (thread->owner_process != this) | ||
| 148 | continue; | ||
| 149 | |||
| 150 | if (thread == GetCurrentThread()) | ||
| 151 | continue; | ||
| 152 | |||
| 153 | // TODO(Subv): When are the other running/ready threads terminated? | ||
| 154 | ASSERT_MSG(thread->status == ThreadStatus::WaitSynchAny || | ||
| 155 | thread->status == ThreadStatus::WaitSynchAll, | ||
| 156 | "Exiting processes with non-waiting threads is currently unimplemented"); | ||
| 157 | |||
| 158 | thread->Stop(); | ||
| 159 | } | ||
| 160 | }; | ||
| 161 | |||
| 162 | auto& system = Core::System::GetInstance(); | ||
| 163 | stop_threads(system.Scheduler(0)->GetThreadList()); | ||
| 164 | stop_threads(system.Scheduler(1)->GetThreadList()); | ||
| 165 | stop_threads(system.Scheduler(2)->GetThreadList()); | ||
| 166 | stop_threads(system.Scheduler(3)->GetThreadList()); | ||
| 167 | } | ||
| 168 | |||
| 169 | /** | ||
| 170 | * Finds a free location for the TLS section of a thread. | ||
| 171 | * @param tls_slots The TLS page array of the thread's owner process. | ||
| 172 | * Returns a tuple of (page, slot, alloc_needed) where: | ||
| 173 | * page: The index of the first allocated TLS page that has free slots. | ||
| 174 | * slot: The index of the first free slot in the indicated page. | ||
| 175 | * alloc_needed: Whether there's a need to allocate a new TLS page (All pages are full). | ||
| 176 | */ | ||
| 177 | static std::tuple<std::size_t, std::size_t, bool> FindFreeThreadLocalSlot( | ||
| 178 | const std::vector<std::bitset<8>>& tls_slots) { | ||
| 179 | // Iterate over all the allocated pages, and try to find one where not all slots are used. | ||
| 180 | for (std::size_t page = 0; page < tls_slots.size(); ++page) { | ||
| 181 | const auto& page_tls_slots = tls_slots[page]; | ||
| 182 | if (!page_tls_slots.all()) { | ||
| 183 | // We found a page with at least one free slot, find which slot it is | ||
| 184 | for (std::size_t slot = 0; slot < page_tls_slots.size(); ++slot) { | ||
| 185 | if (!page_tls_slots.test(slot)) { | ||
| 186 | return std::make_tuple(page, slot, false); | ||
| 187 | } | ||
| 188 | } | ||
| 189 | } | ||
| 190 | } | ||
| 191 | |||
| 192 | return std::make_tuple(0, 0, true); | ||
| 193 | } | ||
| 194 | |||
| 195 | VAddr Process::MarkNextAvailableTLSSlotAsUsed(Thread& thread) { | ||
| 196 | auto [available_page, available_slot, needs_allocation] = FindFreeThreadLocalSlot(tls_slots); | ||
| 197 | const VAddr tls_begin = vm_manager.GetTLSIORegionBaseAddress(); | ||
| 198 | |||
| 199 | if (needs_allocation) { | ||
| 200 | tls_slots.emplace_back(0); // The page is completely available at the start | ||
| 201 | available_page = tls_slots.size() - 1; | ||
| 202 | available_slot = 0; // Use the first slot in the new page | ||
| 203 | |||
| 204 | // Allocate some memory from the end of the linear heap for this region. | ||
| 205 | auto& tls_memory = thread.GetTLSMemory(); | ||
| 206 | tls_memory->insert(tls_memory->end(), Memory::PAGE_SIZE, 0); | ||
| 207 | |||
| 208 | vm_manager.RefreshMemoryBlockMappings(tls_memory.get()); | ||
| 209 | |||
| 210 | vm_manager.MapMemoryBlock(tls_begin + available_page * Memory::PAGE_SIZE, tls_memory, 0, | ||
| 211 | Memory::PAGE_SIZE, MemoryState::ThreadLocal); | ||
| 212 | } | ||
| 213 | |||
| 214 | tls_slots[available_page].set(available_slot); | ||
| 215 | |||
| 216 | return tls_begin + available_page * Memory::PAGE_SIZE + available_slot * Memory::TLS_ENTRY_SIZE; | ||
| 217 | } | ||
| 218 | |||
| 219 | void Process::FreeTLSSlot(VAddr tls_address) { | ||
| 220 | const VAddr tls_base = tls_address - vm_manager.GetTLSIORegionBaseAddress(); | ||
| 221 | const VAddr tls_page = tls_base / Memory::PAGE_SIZE; | ||
| 222 | const VAddr tls_slot = (tls_base % Memory::PAGE_SIZE) / Memory::TLS_ENTRY_SIZE; | ||
| 223 | |||
| 224 | tls_slots[tls_page].reset(tls_slot); | ||
| 129 | } | 225 | } |
| 130 | 226 | ||
| 131 | void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) { | 227 | void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) { |
| @@ -145,8 +241,8 @@ void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) { | |||
| 145 | } | 241 | } |
| 146 | 242 | ||
| 147 | ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) { | 243 | ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) { |
| 148 | if (target < Memory::HEAP_VADDR || target + size > Memory::HEAP_VADDR_END || | 244 | if (target < vm_manager.GetHeapRegionBaseAddress() || |
| 149 | target + size < target) { | 245 | target + size > vm_manager.GetHeapRegionEndAddress() || target + size < target) { |
| 150 | return ERR_INVALID_ADDRESS; | 246 | return ERR_INVALID_ADDRESS; |
| 151 | } | 247 | } |
| 152 | 248 | ||
| @@ -181,8 +277,8 @@ ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission per | |||
| 181 | } | 277 | } |
| 182 | 278 | ||
| 183 | ResultCode Process::HeapFree(VAddr target, u32 size) { | 279 | ResultCode Process::HeapFree(VAddr target, u32 size) { |
| 184 | if (target < Memory::HEAP_VADDR || target + size > Memory::HEAP_VADDR_END || | 280 | if (target < vm_manager.GetHeapRegionBaseAddress() || |
| 185 | target + size < target) { | 281 | target + size > vm_manager.GetHeapRegionEndAddress() || target + size < target) { |
| 186 | return ERR_INVALID_ADDRESS; | 282 | return ERR_INVALID_ADDRESS; |
| 187 | } | 283 | } |
| 188 | 284 | ||
| @@ -211,7 +307,7 @@ ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) { | |||
| 211 | "Shared memory exceeds bounds of mapped block"); | 307 | "Shared memory exceeds bounds of mapped block"); |
| 212 | 308 | ||
| 213 | const std::shared_ptr<std::vector<u8>>& backing_block = vma->second.backing_block; | 309 | const std::shared_ptr<std::vector<u8>>& backing_block = vma->second.backing_block; |
| 214 | size_t backing_block_offset = vma->second.offset + vma_offset; | 310 | std::size_t backing_block_offset = vma->second.offset + vma_offset; |
| 215 | 311 | ||
| 216 | CASCADE_RESULT(auto new_vma, | 312 | CASCADE_RESULT(auto new_vma, |
| 217 | vm_manager.MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size, | 313 | vm_manager.MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size, |
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index 1587d40c1..590e0c73d 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h | |||
| @@ -17,6 +17,10 @@ | |||
| 17 | #include "core/hle/kernel/thread.h" | 17 | #include "core/hle/kernel/thread.h" |
| 18 | #include "core/hle/kernel/vm_manager.h" | 18 | #include "core/hle/kernel/vm_manager.h" |
| 19 | 19 | ||
| 20 | namespace FileSys { | ||
| 21 | class ProgramMetadata; | ||
| 22 | } | ||
| 23 | |||
| 20 | namespace Kernel { | 24 | namespace Kernel { |
| 21 | 25 | ||
| 22 | class KernelCore; | 26 | class KernelCore; |
| @@ -59,7 +63,7 @@ class ResourceLimit; | |||
| 59 | 63 | ||
| 60 | struct CodeSet final : public Object { | 64 | struct CodeSet final : public Object { |
| 61 | struct Segment { | 65 | struct Segment { |
| 62 | size_t offset = 0; | 66 | std::size_t offset = 0; |
| 63 | VAddr addr = 0; | 67 | VAddr addr = 0; |
| 64 | u32 size = 0; | 68 | u32 size = 0; |
| 65 | }; | 69 | }; |
| @@ -131,6 +135,121 @@ public: | |||
| 131 | return HANDLE_TYPE; | 135 | return HANDLE_TYPE; |
| 132 | } | 136 | } |
| 133 | 137 | ||
| 138 | /// Gets a reference to the process' memory manager. | ||
| 139 | Kernel::VMManager& VMManager() { | ||
| 140 | return vm_manager; | ||
| 141 | } | ||
| 142 | |||
| 143 | /// Gets a const reference to the process' memory manager. | ||
| 144 | const Kernel::VMManager& VMManager() const { | ||
| 145 | return vm_manager; | ||
| 146 | } | ||
| 147 | |||
| 148 | /// Gets the current status of the process | ||
| 149 | ProcessStatus GetStatus() const { | ||
| 150 | return status; | ||
| 151 | } | ||
| 152 | |||
| 153 | /// Gets the unique ID that identifies this particular process. | ||
| 154 | u32 GetProcessID() const { | ||
| 155 | return process_id; | ||
| 156 | } | ||
| 157 | |||
| 158 | /// Gets the title ID corresponding to this process. | ||
| 159 | u64 GetTitleID() const { | ||
| 160 | return program_id; | ||
| 161 | } | ||
| 162 | |||
| 163 | /// Gets the resource limit descriptor for this process | ||
| 164 | ResourceLimit& GetResourceLimit() { | ||
| 165 | return *resource_limit; | ||
| 166 | } | ||
| 167 | |||
| 168 | /// Gets the resource limit descriptor for this process | ||
| 169 | const ResourceLimit& GetResourceLimit() const { | ||
| 170 | return *resource_limit; | ||
| 171 | } | ||
| 172 | |||
| 173 | /// Gets the default CPU ID for this process | ||
| 174 | u8 GetDefaultProcessorID() const { | ||
| 175 | return ideal_processor; | ||
| 176 | } | ||
| 177 | |||
| 178 | /// Gets the bitmask of allowed CPUs that this process' threads can run on. | ||
| 179 | u32 GetAllowedProcessorMask() const { | ||
| 180 | return allowed_processor_mask; | ||
| 181 | } | ||
| 182 | |||
| 183 | /// Gets the bitmask of allowed thread priorities. | ||
| 184 | u32 GetAllowedThreadPriorityMask() const { | ||
| 185 | return allowed_thread_priority_mask; | ||
| 186 | } | ||
| 187 | |||
| 188 | u32 IsVirtualMemoryEnabled() const { | ||
| 189 | return is_virtual_address_memory_enabled; | ||
| 190 | } | ||
| 191 | |||
| 192 | /// Whether this process is an AArch64 or AArch32 process. | ||
| 193 | bool Is64BitProcess() const { | ||
| 194 | return is_64bit_process; | ||
| 195 | } | ||
| 196 | |||
| 197 | /** | ||
| 198 | * Loads process-specifics configuration info with metadata provided | ||
| 199 | * by an executable. | ||
| 200 | * | ||
| 201 | * @param metadata The provided metadata to load process specific info. | ||
| 202 | */ | ||
| 203 | void LoadFromMetadata(const FileSys::ProgramMetadata& metadata); | ||
| 204 | |||
| 205 | /** | ||
| 206 | * Parses a list of kernel capability descriptors (as found in the ExHeader) and applies them | ||
| 207 | * to this process. | ||
| 208 | */ | ||
| 209 | void ParseKernelCaps(const u32* kernel_caps, std::size_t len); | ||
| 210 | |||
| 211 | /** | ||
| 212 | * Applies address space changes and launches the process main thread. | ||
| 213 | */ | ||
| 214 | void Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size); | ||
| 215 | |||
| 216 | /** | ||
| 217 | * Prepares a process for termination by stopping all of its threads | ||
| 218 | * and clearing any other resources. | ||
| 219 | */ | ||
| 220 | void PrepareForTermination(); | ||
| 221 | |||
| 222 | void LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr); | ||
| 223 | |||
| 224 | /////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 225 | // Memory Management | ||
| 226 | |||
| 227 | // Marks the next available region as used and returns the address of the slot. | ||
| 228 | VAddr MarkNextAvailableTLSSlotAsUsed(Thread& thread); | ||
| 229 | |||
| 230 | // Frees a used TLS slot identified by the given address | ||
| 231 | void FreeTLSSlot(VAddr tls_address); | ||
| 232 | |||
| 233 | ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms); | ||
| 234 | ResultCode HeapFree(VAddr target, u32 size); | ||
| 235 | |||
| 236 | ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size); | ||
| 237 | |||
| 238 | ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size); | ||
| 239 | |||
| 240 | private: | ||
| 241 | explicit Process(KernelCore& kernel); | ||
| 242 | ~Process() override; | ||
| 243 | |||
| 244 | /// Memory manager for this process. | ||
| 245 | Kernel::VMManager vm_manager; | ||
| 246 | |||
| 247 | /// Current status of the process | ||
| 248 | ProcessStatus status; | ||
| 249 | |||
| 250 | /// The ID of this process | ||
| 251 | u32 process_id = 0; | ||
| 252 | |||
| 134 | /// Title ID corresponding to the process | 253 | /// Title ID corresponding to the process |
| 135 | u64 program_id; | 254 | u64 program_id; |
| 136 | 255 | ||
| @@ -140,7 +259,7 @@ public: | |||
| 140 | /// The process may only call SVCs which have the corresponding bit set. | 259 | /// The process may only call SVCs which have the corresponding bit set. |
| 141 | std::bitset<0x80> svc_access_mask; | 260 | std::bitset<0x80> svc_access_mask; |
| 142 | /// Maximum size of the handle table for the process. | 261 | /// Maximum size of the handle table for the process. |
| 143 | unsigned int handle_table_size = 0x200; | 262 | u32 handle_table_size = 0x200; |
| 144 | /// Special memory ranges mapped into this processes address space. This is used to give | 263 | /// Special memory ranges mapped into this processes address space. This is used to give |
| 145 | /// processes access to specific I/O regions and device memory. | 264 | /// processes access to specific I/O regions and device memory. |
| 146 | boost::container::static_vector<AddressMapping, 8> address_mappings; | 265 | boost::container::static_vector<AddressMapping, 8> address_mappings; |
| @@ -154,29 +273,6 @@ public: | |||
| 154 | u32 allowed_processor_mask = THREADPROCESSORID_DEFAULT_MASK; | 273 | u32 allowed_processor_mask = THREADPROCESSORID_DEFAULT_MASK; |
| 155 | u32 allowed_thread_priority_mask = 0xFFFFFFFF; | 274 | u32 allowed_thread_priority_mask = 0xFFFFFFFF; |
| 156 | u32 is_virtual_address_memory_enabled = 0; | 275 | u32 is_virtual_address_memory_enabled = 0; |
| 157 | /// Current status of the process | ||
| 158 | ProcessStatus status; | ||
| 159 | |||
| 160 | /// The ID of this process | ||
| 161 | u32 process_id = 0; | ||
| 162 | |||
| 163 | /** | ||
| 164 | * Parses a list of kernel capability descriptors (as found in the ExHeader) and applies them | ||
| 165 | * to this process. | ||
| 166 | */ | ||
| 167 | void ParseKernelCaps(const u32* kernel_caps, size_t len); | ||
| 168 | |||
| 169 | /** | ||
| 170 | * Applies address space changes and launches the process main thread. | ||
| 171 | */ | ||
| 172 | void Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size); | ||
| 173 | |||
| 174 | void LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr); | ||
| 175 | |||
| 176 | /////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 177 | // Memory Management | ||
| 178 | |||
| 179 | VMManager vm_manager; | ||
| 180 | 276 | ||
| 181 | // Memory used to back the allocations in the regular heap. A single vector is used to cover | 277 | // Memory used to back the allocations in the regular heap. A single vector is used to cover |
| 182 | // the entire virtual address space extents that bound the allocations, including any holes. | 278 | // the entire virtual address space extents that bound the allocations, including any holes. |
| @@ -196,18 +292,12 @@ public: | |||
| 196 | /// This vector will grow as more pages are allocated for new threads. | 292 | /// This vector will grow as more pages are allocated for new threads. |
| 197 | std::vector<std::bitset<8>> tls_slots; | 293 | std::vector<std::bitset<8>> tls_slots; |
| 198 | 294 | ||
| 199 | std::string name; | 295 | /// Whether or not this process is AArch64, or AArch32. |
| 200 | 296 | /// By default, we currently assume this is true, unless otherwise | |
| 201 | ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms); | 297 | /// specified by metadata provided to the process during loading. |
| 202 | ResultCode HeapFree(VAddr target, u32 size); | 298 | bool is_64bit_process = true; |
| 203 | |||
| 204 | ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size); | ||
| 205 | 299 | ||
| 206 | ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size); | 300 | std::string name; |
| 207 | |||
| 208 | private: | ||
| 209 | explicit Process(KernelCore& kernel); | ||
| 210 | ~Process() override; | ||
| 211 | }; | 301 | }; |
| 212 | 302 | ||
| 213 | } // namespace Kernel | 303 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index 69c812f16..1e82cfffb 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp | |||
| @@ -17,7 +17,7 @@ namespace Kernel { | |||
| 17 | 17 | ||
| 18 | std::mutex Scheduler::scheduler_mutex; | 18 | std::mutex Scheduler::scheduler_mutex; |
| 19 | 19 | ||
| 20 | Scheduler::Scheduler(Core::ARM_Interface* cpu_core) : cpu_core(cpu_core) {} | 20 | Scheduler::Scheduler(Core::ARM_Interface& cpu_core) : cpu_core(cpu_core) {} |
| 21 | 21 | ||
| 22 | Scheduler::~Scheduler() { | 22 | Scheduler::~Scheduler() { |
| 23 | for (auto& thread : thread_list) { | 23 | for (auto& thread : thread_list) { |
| @@ -59,9 +59,9 @@ void Scheduler::SwitchContext(Thread* new_thread) { | |||
| 59 | // Save context for previous thread | 59 | // Save context for previous thread |
| 60 | if (previous_thread) { | 60 | if (previous_thread) { |
| 61 | previous_thread->last_running_ticks = CoreTiming::GetTicks(); | 61 | previous_thread->last_running_ticks = CoreTiming::GetTicks(); |
| 62 | cpu_core->SaveContext(previous_thread->context); | 62 | cpu_core.SaveContext(previous_thread->context); |
| 63 | // Save the TPIDR_EL0 system register in case it was modified. | 63 | // Save the TPIDR_EL0 system register in case it was modified. |
| 64 | previous_thread->tpidr_el0 = cpu_core->GetTPIDR_EL0(); | 64 | previous_thread->tpidr_el0 = cpu_core.GetTPIDR_EL0(); |
| 65 | 65 | ||
| 66 | if (previous_thread->status == ThreadStatus::Running) { | 66 | if (previous_thread->status == ThreadStatus::Running) { |
| 67 | // This is only the case when a reschedule is triggered without the current thread | 67 | // This is only the case when a reschedule is triggered without the current thread |
| @@ -88,13 +88,13 @@ void Scheduler::SwitchContext(Thread* new_thread) { | |||
| 88 | 88 | ||
| 89 | if (previous_process != current_thread->owner_process) { | 89 | if (previous_process != current_thread->owner_process) { |
| 90 | Core::CurrentProcess() = current_thread->owner_process; | 90 | Core::CurrentProcess() = current_thread->owner_process; |
| 91 | SetCurrentPageTable(&Core::CurrentProcess()->vm_manager.page_table); | 91 | SetCurrentPageTable(&Core::CurrentProcess()->VMManager().page_table); |
| 92 | } | 92 | } |
| 93 | 93 | ||
| 94 | cpu_core->LoadContext(new_thread->context); | 94 | cpu_core.LoadContext(new_thread->context); |
| 95 | cpu_core->SetTlsAddress(new_thread->GetTLSAddress()); | 95 | cpu_core.SetTlsAddress(new_thread->GetTLSAddress()); |
| 96 | cpu_core->SetTPIDR_EL0(new_thread->GetTPIDR_EL0()); | 96 | cpu_core.SetTPIDR_EL0(new_thread->GetTPIDR_EL0()); |
| 97 | cpu_core->ClearExclusiveState(); | 97 | cpu_core.ClearExclusiveState(); |
| 98 | } else { | 98 | } else { |
| 99 | current_thread = nullptr; | 99 | current_thread = nullptr; |
| 100 | // Note: We do not reset the current process and current page table when idling because | 100 | // Note: We do not reset the current process and current page table when idling because |
diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index 744990c9b..2c94641ec 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h | |||
| @@ -19,7 +19,7 @@ namespace Kernel { | |||
| 19 | 19 | ||
| 20 | class Scheduler final { | 20 | class Scheduler final { |
| 21 | public: | 21 | public: |
| 22 | explicit Scheduler(Core::ARM_Interface* cpu_core); | 22 | explicit Scheduler(Core::ARM_Interface& cpu_core); |
| 23 | ~Scheduler(); | 23 | ~Scheduler(); |
| 24 | 24 | ||
| 25 | /// Returns whether there are any threads that are ready to run. | 25 | /// Returns whether there are any threads that are ready to run. |
| @@ -72,7 +72,7 @@ private: | |||
| 72 | 72 | ||
| 73 | SharedPtr<Thread> current_thread = nullptr; | 73 | SharedPtr<Thread> current_thread = nullptr; |
| 74 | 74 | ||
| 75 | Core::ARM_Interface* cpu_core; | 75 | Core::ARM_Interface& cpu_core; |
| 76 | 76 | ||
| 77 | static std::mutex scheduler_mutex; | 77 | static std::mutex scheduler_mutex; |
| 78 | }; | 78 | }; |
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index abb1d09cd..d061e6155 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 9 | #include "core/core.h" | 9 | #include "core/core.h" |
| 10 | #include "core/hle/kernel/errors.h" | 10 | #include "core/hle/kernel/errors.h" |
| 11 | #include "core/hle/kernel/kernel.h" | ||
| 11 | #include "core/hle/kernel/shared_memory.h" | 12 | #include "core/hle/kernel/shared_memory.h" |
| 12 | #include "core/memory.h" | 13 | #include "core/memory.h" |
| 13 | 14 | ||
| @@ -34,11 +35,11 @@ SharedPtr<SharedMemory> SharedMemory::Create(KernelCore& kernel, SharedPtr<Proce | |||
| 34 | 35 | ||
| 35 | // Refresh the address mappings for the current process. | 36 | // Refresh the address mappings for the current process. |
| 36 | if (Core::CurrentProcess() != nullptr) { | 37 | if (Core::CurrentProcess() != nullptr) { |
| 37 | Core::CurrentProcess()->vm_manager.RefreshMemoryBlockMappings( | 38 | Core::CurrentProcess()->VMManager().RefreshMemoryBlockMappings( |
| 38 | shared_memory->backing_block.get()); | 39 | shared_memory->backing_block.get()); |
| 39 | } | 40 | } |
| 40 | } else { | 41 | } else { |
| 41 | auto& vm_manager = shared_memory->owner_process->vm_manager; | 42 | auto& vm_manager = shared_memory->owner_process->VMManager(); |
| 42 | 43 | ||
| 43 | // The memory is already available and mapped in the owner process. | 44 | // The memory is already available and mapped in the owner process. |
| 44 | auto vma = vm_manager.FindVMA(address); | 45 | auto vma = vm_manager.FindVMA(address); |
| @@ -71,7 +72,8 @@ SharedPtr<SharedMemory> SharedMemory::CreateForApplet( | |||
| 71 | shared_memory->other_permissions = other_permissions; | 72 | shared_memory->other_permissions = other_permissions; |
| 72 | shared_memory->backing_block = std::move(heap_block); | 73 | shared_memory->backing_block = std::move(heap_block); |
| 73 | shared_memory->backing_block_offset = offset; | 74 | shared_memory->backing_block_offset = offset; |
| 74 | shared_memory->base_address = Memory::HEAP_VADDR + offset; | 75 | shared_memory->base_address = |
| 76 | kernel.CurrentProcess()->VMManager().GetHeapRegionBaseAddress() + offset; | ||
| 75 | 77 | ||
| 76 | return shared_memory; | 78 | return shared_memory; |
| 77 | } | 79 | } |
| @@ -105,7 +107,7 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi | |||
| 105 | VAddr target_address = address; | 107 | VAddr target_address = address; |
| 106 | 108 | ||
| 107 | // Map the memory block into the target process | 109 | // Map the memory block into the target process |
| 108 | auto result = target_process->vm_manager.MapMemoryBlock( | 110 | auto result = target_process->VMManager().MapMemoryBlock( |
| 109 | target_address, backing_block, backing_block_offset, size, MemoryState::Shared); | 111 | target_address, backing_block, backing_block_offset, size, MemoryState::Shared); |
| 110 | if (result.Failed()) { | 112 | if (result.Failed()) { |
| 111 | LOG_ERROR( | 113 | LOG_ERROR( |
| @@ -115,14 +117,14 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi | |||
| 115 | return result.Code(); | 117 | return result.Code(); |
| 116 | } | 118 | } |
| 117 | 119 | ||
| 118 | return target_process->vm_manager.ReprotectRange(target_address, size, | 120 | return target_process->VMManager().ReprotectRange(target_address, size, |
| 119 | ConvertPermissions(permissions)); | 121 | ConvertPermissions(permissions)); |
| 120 | } | 122 | } |
| 121 | 123 | ||
| 122 | ResultCode SharedMemory::Unmap(Process* target_process, VAddr address) { | 124 | ResultCode SharedMemory::Unmap(Process* target_process, VAddr address) { |
| 123 | // TODO(Subv): Verify what happens if the application tries to unmap an address that is not | 125 | // TODO(Subv): Verify what happens if the application tries to unmap an address that is not |
| 124 | // mapped to a SharedMemory. | 126 | // mapped to a SharedMemory. |
| 125 | return target_process->vm_manager.UnmapRange(address, size); | 127 | return target_process->VMManager().UnmapRange(address, size); |
| 126 | } | 128 | } |
| 127 | 129 | ||
| 128 | VMAPermission SharedMemory::ConvertPermissions(MemoryPermission permission) { | 130 | VMAPermission SharedMemory::ConvertPermissions(MemoryPermission permission) { |
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h index 2c729afe3..2c06bb7ce 100644 --- a/src/core/hle/kernel/shared_memory.h +++ b/src/core/hle/kernel/shared_memory.h | |||
| @@ -119,7 +119,7 @@ public: | |||
| 119 | /// Backing memory for this shared memory block. | 119 | /// Backing memory for this shared memory block. |
| 120 | std::shared_ptr<std::vector<u8>> backing_block; | 120 | std::shared_ptr<std::vector<u8>> backing_block; |
| 121 | /// Offset into the backing block for this shared memory. | 121 | /// Offset into the backing block for this shared memory. |
| 122 | size_t backing_block_offset; | 122 | std::size_t backing_block_offset; |
| 123 | /// Size of the memory block. Page-aligned. | 123 | /// Size of the memory block. Page-aligned. |
| 124 | u64 size; | 124 | u64 size; |
| 125 | /// Permission restrictions applied to the process which created the block. | 125 | /// Permission restrictions applied to the process which created the block. |
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index f500fd2e7..1cdaa740a 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -35,13 +35,25 @@ | |||
| 35 | #include "core/hle/service/service.h" | 35 | #include "core/hle/service/service.h" |
| 36 | 36 | ||
| 37 | namespace Kernel { | 37 | namespace Kernel { |
| 38 | namespace { | ||
| 39 | constexpr bool Is4KBAligned(VAddr address) { | ||
| 40 | return (address & 0xFFF) == 0; | ||
| 41 | } | ||
| 42 | } // Anonymous namespace | ||
| 38 | 43 | ||
| 39 | /// Set the process heap to a given Size. It can both extend and shrink the heap. | 44 | /// Set the process heap to a given Size. It can both extend and shrink the heap. |
| 40 | static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) { | 45 | static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) { |
| 41 | LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", heap_size); | 46 | LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", heap_size); |
| 47 | |||
| 48 | // Size must be a multiple of 0x200000 (2MB) and be equal to or less than 4GB. | ||
| 49 | if ((heap_size & 0xFFFFFFFE001FFFFF) != 0) { | ||
| 50 | return ERR_INVALID_SIZE; | ||
| 51 | } | ||
| 52 | |||
| 42 | auto& process = *Core::CurrentProcess(); | 53 | auto& process = *Core::CurrentProcess(); |
| 54 | const VAddr heap_base = process.VMManager().GetHeapRegionBaseAddress(); | ||
| 43 | CASCADE_RESULT(*heap_addr, | 55 | CASCADE_RESULT(*heap_addr, |
| 44 | process.HeapAllocate(Memory::HEAP_VADDR, heap_size, VMAPermission::ReadWrite)); | 56 | process.HeapAllocate(heap_base, heap_size, VMAPermission::ReadWrite)); |
| 45 | return RESULT_SUCCESS; | 57 | return RESULT_SUCCESS; |
| 46 | } | 58 | } |
| 47 | 59 | ||
| @@ -56,6 +68,15 @@ static ResultCode SetMemoryAttribute(VAddr addr, u64 size, u32 state0, u32 state | |||
| 56 | static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { | 68 | static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { |
| 57 | LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, | 69 | LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, |
| 58 | src_addr, size); | 70 | src_addr, size); |
| 71 | |||
| 72 | if (!Is4KBAligned(dst_addr) || !Is4KBAligned(src_addr)) { | ||
| 73 | return ERR_INVALID_ADDRESS; | ||
| 74 | } | ||
| 75 | |||
| 76 | if (size == 0 || !Is4KBAligned(size)) { | ||
| 77 | return ERR_INVALID_SIZE; | ||
| 78 | } | ||
| 79 | |||
| 59 | return Core::CurrentProcess()->MirrorMemory(dst_addr, src_addr, size); | 80 | return Core::CurrentProcess()->MirrorMemory(dst_addr, src_addr, size); |
| 60 | } | 81 | } |
| 61 | 82 | ||
| @@ -63,6 +84,15 @@ static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { | |||
| 63 | static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { | 84 | static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { |
| 64 | LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, | 85 | LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr, |
| 65 | src_addr, size); | 86 | src_addr, size); |
| 87 | |||
| 88 | if (!Is4KBAligned(dst_addr) || !Is4KBAligned(src_addr)) { | ||
| 89 | return ERR_INVALID_ADDRESS; | ||
| 90 | } | ||
| 91 | |||
| 92 | if (size == 0 || !Is4KBAligned(size)) { | ||
| 93 | return ERR_INVALID_SIZE; | ||
| 94 | } | ||
| 95 | |||
| 66 | return Core::CurrentProcess()->UnmapMemory(dst_addr, src_addr, size); | 96 | return Core::CurrentProcess()->UnmapMemory(dst_addr, src_addr, size); |
| 67 | } | 97 | } |
| 68 | 98 | ||
| @@ -140,13 +170,13 @@ static ResultCode GetProcessId(u32* process_id, Handle process_handle) { | |||
| 140 | return ERR_INVALID_HANDLE; | 170 | return ERR_INVALID_HANDLE; |
| 141 | } | 171 | } |
| 142 | 172 | ||
| 143 | *process_id = process->process_id; | 173 | *process_id = process->GetProcessID(); |
| 144 | return RESULT_SUCCESS; | 174 | return RESULT_SUCCESS; |
| 145 | } | 175 | } |
| 146 | 176 | ||
| 147 | /// Default thread wakeup callback for WaitSynchronization | 177 | /// Default thread wakeup callback for WaitSynchronization |
| 148 | static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> thread, | 178 | static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> thread, |
| 149 | SharedPtr<WaitObject> object, size_t index) { | 179 | SharedPtr<WaitObject> object, std::size_t index) { |
| 150 | ASSERT(thread->status == ThreadStatus::WaitSynchAny); | 180 | ASSERT(thread->status == ThreadStatus::WaitSynchAny); |
| 151 | 181 | ||
| 152 | if (reason == ThreadWakeupReason::Timeout) { | 182 | if (reason == ThreadWakeupReason::Timeout) { |
| @@ -251,6 +281,10 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr, | |||
| 251 | "requesting_current_thread_handle=0x{:08X}", | 281 | "requesting_current_thread_handle=0x{:08X}", |
| 252 | holding_thread_handle, mutex_addr, requesting_thread_handle); | 282 | holding_thread_handle, mutex_addr, requesting_thread_handle); |
| 253 | 283 | ||
| 284 | if (Memory::IsKernelVirtualAddress(mutex_addr)) { | ||
| 285 | return ERR_INVALID_ADDRESS_STATE; | ||
| 286 | } | ||
| 287 | |||
| 254 | auto& handle_table = Core::System::GetInstance().Kernel().HandleTable(); | 288 | auto& handle_table = Core::System::GetInstance().Kernel().HandleTable(); |
| 255 | return Mutex::TryAcquire(handle_table, mutex_addr, holding_thread_handle, | 289 | return Mutex::TryAcquire(handle_table, mutex_addr, holding_thread_handle, |
| 256 | requesting_thread_handle); | 290 | requesting_thread_handle); |
| @@ -260,6 +294,10 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr, | |||
| 260 | static ResultCode ArbitrateUnlock(VAddr mutex_addr) { | 294 | static ResultCode ArbitrateUnlock(VAddr mutex_addr) { |
| 261 | LOG_TRACE(Kernel_SVC, "called mutex_addr=0x{:X}", mutex_addr); | 295 | LOG_TRACE(Kernel_SVC, "called mutex_addr=0x{:X}", mutex_addr); |
| 262 | 296 | ||
| 297 | if (Memory::IsKernelVirtualAddress(mutex_addr)) { | ||
| 298 | return ERR_INVALID_ADDRESS_STATE; | ||
| 299 | } | ||
| 300 | |||
| 263 | return Mutex::Release(mutex_addr); | 301 | return Mutex::Release(mutex_addr); |
| 264 | } | 302 | } |
| 265 | 303 | ||
| @@ -288,26 +326,27 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) | |||
| 288 | LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id, | 326 | LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id, |
| 289 | info_sub_id, handle); | 327 | info_sub_id, handle); |
| 290 | 328 | ||
| 291 | const auto& vm_manager = Core::CurrentProcess()->vm_manager; | 329 | const auto& current_process = Core::CurrentProcess(); |
| 330 | const auto& vm_manager = current_process->VMManager(); | ||
| 292 | 331 | ||
| 293 | switch (static_cast<GetInfoType>(info_id)) { | 332 | switch (static_cast<GetInfoType>(info_id)) { |
| 294 | case GetInfoType::AllowedCpuIdBitmask: | 333 | case GetInfoType::AllowedCpuIdBitmask: |
| 295 | *result = Core::CurrentProcess()->allowed_processor_mask; | 334 | *result = current_process->GetAllowedProcessorMask(); |
| 296 | break; | 335 | break; |
| 297 | case GetInfoType::AllowedThreadPrioBitmask: | 336 | case GetInfoType::AllowedThreadPrioBitmask: |
| 298 | *result = Core::CurrentProcess()->allowed_thread_priority_mask; | 337 | *result = current_process->GetAllowedThreadPriorityMask(); |
| 299 | break; | 338 | break; |
| 300 | case GetInfoType::MapRegionBaseAddr: | 339 | case GetInfoType::MapRegionBaseAddr: |
| 301 | *result = Memory::MAP_REGION_VADDR; | 340 | *result = vm_manager.GetMapRegionBaseAddress(); |
| 302 | break; | 341 | break; |
| 303 | case GetInfoType::MapRegionSize: | 342 | case GetInfoType::MapRegionSize: |
| 304 | *result = Memory::MAP_REGION_SIZE; | 343 | *result = vm_manager.GetMapRegionSize(); |
| 305 | break; | 344 | break; |
| 306 | case GetInfoType::HeapRegionBaseAddr: | 345 | case GetInfoType::HeapRegionBaseAddr: |
| 307 | *result = Memory::HEAP_VADDR; | 346 | *result = vm_manager.GetHeapRegionBaseAddress(); |
| 308 | break; | 347 | break; |
| 309 | case GetInfoType::HeapRegionSize: | 348 | case GetInfoType::HeapRegionSize: |
| 310 | *result = Memory::HEAP_SIZE; | 349 | *result = vm_manager.GetHeapRegionSize(); |
| 311 | break; | 350 | break; |
| 312 | case GetInfoType::TotalMemoryUsage: | 351 | case GetInfoType::TotalMemoryUsage: |
| 313 | *result = vm_manager.GetTotalMemoryUsage(); | 352 | *result = vm_manager.GetTotalMemoryUsage(); |
| @@ -322,22 +361,35 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) | |||
| 322 | *result = 0; | 361 | *result = 0; |
| 323 | break; | 362 | break; |
| 324 | case GetInfoType::AddressSpaceBaseAddr: | 363 | case GetInfoType::AddressSpaceBaseAddr: |
| 325 | *result = vm_manager.GetAddressSpaceBaseAddr(); | 364 | *result = vm_manager.GetCodeRegionBaseAddress(); |
| 326 | break; | 365 | break; |
| 327 | case GetInfoType::AddressSpaceSize: | 366 | case GetInfoType::AddressSpaceSize: { |
| 328 | *result = vm_manager.GetAddressSpaceSize(); | 367 | const u64 width = vm_manager.GetAddressSpaceWidth(); |
| 368 | |||
| 369 | switch (width) { | ||
| 370 | case 32: | ||
| 371 | *result = 0xFFE00000; | ||
| 372 | break; | ||
| 373 | case 36: | ||
| 374 | *result = 0xFF8000000; | ||
| 375 | break; | ||
| 376 | case 39: | ||
| 377 | *result = 0x7FF8000000; | ||
| 378 | break; | ||
| 379 | } | ||
| 329 | break; | 380 | break; |
| 381 | } | ||
| 330 | case GetInfoType::NewMapRegionBaseAddr: | 382 | case GetInfoType::NewMapRegionBaseAddr: |
| 331 | *result = Memory::NEW_MAP_REGION_VADDR; | 383 | *result = vm_manager.GetNewMapRegionBaseAddress(); |
| 332 | break; | 384 | break; |
| 333 | case GetInfoType::NewMapRegionSize: | 385 | case GetInfoType::NewMapRegionSize: |
| 334 | *result = Memory::NEW_MAP_REGION_SIZE; | 386 | *result = vm_manager.GetNewMapRegionSize(); |
| 335 | break; | 387 | break; |
| 336 | case GetInfoType::IsVirtualAddressMemoryEnabled: | 388 | case GetInfoType::IsVirtualAddressMemoryEnabled: |
| 337 | *result = Core::CurrentProcess()->is_virtual_address_memory_enabled; | 389 | *result = current_process->IsVirtualMemoryEnabled(); |
| 338 | break; | 390 | break; |
| 339 | case GetInfoType::TitleId: | 391 | case GetInfoType::TitleId: |
| 340 | *result = Core::CurrentProcess()->program_id; | 392 | *result = current_process->GetTitleID(); |
| 341 | break; | 393 | break; |
| 342 | case GetInfoType::PrivilegedProcessId: | 394 | case GetInfoType::PrivilegedProcessId: |
| 343 | LOG_WARNING(Kernel_SVC, | 395 | LOG_WARNING(Kernel_SVC, |
| @@ -363,8 +415,36 @@ static ResultCode SetThreadActivity(Handle handle, u32 unknown) { | |||
| 363 | } | 415 | } |
| 364 | 416 | ||
| 365 | /// Gets the thread context | 417 | /// Gets the thread context |
| 366 | static ResultCode GetThreadContext(Handle handle, VAddr addr) { | 418 | static ResultCode GetThreadContext(VAddr thread_context, Handle handle) { |
| 367 | LOG_WARNING(Kernel_SVC, "(STUBBED) called, handle=0x{:08X}, addr=0x{:X}", handle, addr); | 419 | LOG_DEBUG(Kernel_SVC, "called, context=0x{:08X}, thread=0x{:X}", thread_context, handle); |
| 420 | |||
| 421 | auto& kernel = Core::System::GetInstance().Kernel(); | ||
| 422 | const SharedPtr<Thread> thread = kernel.HandleTable().Get<Thread>(handle); | ||
| 423 | if (!thread) { | ||
| 424 | return ERR_INVALID_HANDLE; | ||
| 425 | } | ||
| 426 | |||
| 427 | const auto current_process = Core::CurrentProcess(); | ||
| 428 | if (thread->owner_process != current_process) { | ||
| 429 | return ERR_INVALID_HANDLE; | ||
| 430 | } | ||
| 431 | |||
| 432 | if (thread == GetCurrentThread()) { | ||
| 433 | return ERR_ALREADY_REGISTERED; | ||
| 434 | } | ||
| 435 | |||
| 436 | Core::ARM_Interface::ThreadContext ctx = thread->context; | ||
| 437 | // Mask away mode bits, interrupt bits, IL bit, and other reserved bits. | ||
| 438 | ctx.pstate &= 0xFF0FFE20; | ||
| 439 | |||
| 440 | // If 64-bit, we can just write the context registers directly and we're good. | ||
| 441 | // However, if 32-bit, we have to ensure some registers are zeroed out. | ||
| 442 | if (!current_process->Is64BitProcess()) { | ||
| 443 | std::fill(ctx.cpu_registers.begin() + 15, ctx.cpu_registers.end(), 0); | ||
| 444 | std::fill(ctx.vector_registers.begin() + 16, ctx.vector_registers.end(), u128{}); | ||
| 445 | } | ||
| 446 | |||
| 447 | Memory::WriteBlock(thread_context, &ctx, sizeof(ctx)); | ||
| 368 | return RESULT_SUCCESS; | 448 | return RESULT_SUCCESS; |
| 369 | } | 449 | } |
| 370 | 450 | ||
| @@ -392,8 +472,8 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) { | |||
| 392 | 472 | ||
| 393 | // Note: The kernel uses the current process's resource limit instead of | 473 | // Note: The kernel uses the current process's resource limit instead of |
| 394 | // the one from the thread owner's resource limit. | 474 | // the one from the thread owner's resource limit. |
| 395 | SharedPtr<ResourceLimit>& resource_limit = Core::CurrentProcess()->resource_limit; | 475 | const ResourceLimit& resource_limit = Core::CurrentProcess()->GetResourceLimit(); |
| 396 | if (resource_limit->GetMaxResourceValue(ResourceType::Priority) > priority) { | 476 | if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { |
| 397 | return ERR_NOT_AUTHORIZED; | 477 | return ERR_NOT_AUTHORIZED; |
| 398 | } | 478 | } |
| 399 | 479 | ||
| @@ -415,35 +495,43 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s | |||
| 415 | "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}", | 495 | "called, shared_memory_handle=0x{:X}, addr=0x{:X}, size=0x{:X}, permissions=0x{:08X}", |
| 416 | shared_memory_handle, addr, size, permissions); | 496 | shared_memory_handle, addr, size, permissions); |
| 417 | 497 | ||
| 498 | if (!Is4KBAligned(addr)) { | ||
| 499 | return ERR_INVALID_ADDRESS; | ||
| 500 | } | ||
| 501 | |||
| 502 | if (size == 0 || !Is4KBAligned(size)) { | ||
| 503 | return ERR_INVALID_SIZE; | ||
| 504 | } | ||
| 505 | |||
| 506 | const auto permissions_type = static_cast<MemoryPermission>(permissions); | ||
| 507 | if (permissions_type != MemoryPermission::Read && | ||
| 508 | permissions_type != MemoryPermission::ReadWrite) { | ||
| 509 | LOG_ERROR(Kernel_SVC, "Invalid permissions=0x{:08X}", permissions); | ||
| 510 | return ERR_INVALID_MEMORY_PERMISSIONS; | ||
| 511 | } | ||
| 512 | |||
| 418 | auto& kernel = Core::System::GetInstance().Kernel(); | 513 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 419 | auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle); | 514 | auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle); |
| 420 | if (!shared_memory) { | 515 | if (!shared_memory) { |
| 421 | return ERR_INVALID_HANDLE; | 516 | return ERR_INVALID_HANDLE; |
| 422 | } | 517 | } |
| 423 | 518 | ||
| 424 | MemoryPermission permissions_type = static_cast<MemoryPermission>(permissions); | 519 | return shared_memory->Map(Core::CurrentProcess().get(), addr, permissions_type, |
| 425 | switch (permissions_type) { | 520 | MemoryPermission::DontCare); |
| 426 | case MemoryPermission::Read: | ||
| 427 | case MemoryPermission::Write: | ||
| 428 | case MemoryPermission::ReadWrite: | ||
| 429 | case MemoryPermission::Execute: | ||
| 430 | case MemoryPermission::ReadExecute: | ||
| 431 | case MemoryPermission::WriteExecute: | ||
| 432 | case MemoryPermission::ReadWriteExecute: | ||
| 433 | case MemoryPermission::DontCare: | ||
| 434 | return shared_memory->Map(Core::CurrentProcess().get(), addr, permissions_type, | ||
| 435 | MemoryPermission::DontCare); | ||
| 436 | default: | ||
| 437 | LOG_ERROR(Kernel_SVC, "unknown permissions=0x{:08X}", permissions); | ||
| 438 | } | ||
| 439 | |||
| 440 | return RESULT_SUCCESS; | ||
| 441 | } | 521 | } |
| 442 | 522 | ||
| 443 | static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) { | 523 | static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) { |
| 444 | LOG_WARNING(Kernel_SVC, "called, shared_memory_handle=0x{:08X}, addr=0x{:X}, size=0x{:X}", | 524 | LOG_WARNING(Kernel_SVC, "called, shared_memory_handle=0x{:08X}, addr=0x{:X}, size=0x{:X}", |
| 445 | shared_memory_handle, addr, size); | 525 | shared_memory_handle, addr, size); |
| 446 | 526 | ||
| 527 | if (!Is4KBAligned(addr)) { | ||
| 528 | return ERR_INVALID_ADDRESS; | ||
| 529 | } | ||
| 530 | |||
| 531 | if (size == 0 || !Is4KBAligned(size)) { | ||
| 532 | return ERR_INVALID_SIZE; | ||
| 533 | } | ||
| 534 | |||
| 447 | auto& kernel = Core::System::GetInstance().Kernel(); | 535 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 448 | auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle); | 536 | auto shared_memory = kernel.HandleTable().Get<SharedMemory>(shared_memory_handle); |
| 449 | 537 | ||
| @@ -459,9 +547,9 @@ static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_i | |||
| 459 | if (!process) { | 547 | if (!process) { |
| 460 | return ERR_INVALID_HANDLE; | 548 | return ERR_INVALID_HANDLE; |
| 461 | } | 549 | } |
| 462 | auto vma = process->vm_manager.FindVMA(addr); | 550 | auto vma = process->VMManager().FindVMA(addr); |
| 463 | memory_info->attributes = 0; | 551 | memory_info->attributes = 0; |
| 464 | if (vma == Core::CurrentProcess()->vm_manager.vma_map.end()) { | 552 | if (vma == Core::CurrentProcess()->VMManager().vma_map.end()) { |
| 465 | memory_info->base_address = 0; | 553 | memory_info->base_address = 0; |
| 466 | memory_info->permission = static_cast<u32>(VMAPermission::None); | 554 | memory_info->permission = static_cast<u32>(VMAPermission::None); |
| 467 | memory_info->size = 0; | 555 | memory_info->size = 0; |
| @@ -485,35 +573,13 @@ static ResultCode QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, VAdd | |||
| 485 | 573 | ||
| 486 | /// Exits the current process | 574 | /// Exits the current process |
| 487 | static void ExitProcess() { | 575 | static void ExitProcess() { |
| 488 | LOG_INFO(Kernel_SVC, "Process {} exiting", Core::CurrentProcess()->process_id); | 576 | auto& current_process = Core::CurrentProcess(); |
| 489 | 577 | ||
| 490 | ASSERT_MSG(Core::CurrentProcess()->status == ProcessStatus::Running, | 578 | LOG_INFO(Kernel_SVC, "Process {} exiting", current_process->GetProcessID()); |
| 579 | ASSERT_MSG(current_process->GetStatus() == ProcessStatus::Running, | ||
| 491 | "Process has already exited"); | 580 | "Process has already exited"); |
| 492 | 581 | ||
| 493 | Core::CurrentProcess()->status = ProcessStatus::Exited; | 582 | current_process->PrepareForTermination(); |
| 494 | |||
| 495 | auto stop_threads = [](const std::vector<SharedPtr<Thread>>& thread_list) { | ||
| 496 | for (auto& thread : thread_list) { | ||
| 497 | if (thread->owner_process != Core::CurrentProcess()) | ||
| 498 | continue; | ||
| 499 | |||
| 500 | if (thread == GetCurrentThread()) | ||
| 501 | continue; | ||
| 502 | |||
| 503 | // TODO(Subv): When are the other running/ready threads terminated? | ||
| 504 | ASSERT_MSG(thread->status == ThreadStatus::WaitSynchAny || | ||
| 505 | thread->status == ThreadStatus::WaitSynchAll, | ||
| 506 | "Exiting processes with non-waiting threads is currently unimplemented"); | ||
| 507 | |||
| 508 | thread->Stop(); | ||
| 509 | } | ||
| 510 | }; | ||
| 511 | |||
| 512 | auto& system = Core::System::GetInstance(); | ||
| 513 | stop_threads(system.Scheduler(0)->GetThreadList()); | ||
| 514 | stop_threads(system.Scheduler(1)->GetThreadList()); | ||
| 515 | stop_threads(system.Scheduler(2)->GetThreadList()); | ||
| 516 | stop_threads(system.Scheduler(3)->GetThreadList()); | ||
| 517 | 583 | ||
| 518 | // Kill the current thread | 584 | // Kill the current thread |
| 519 | GetCurrentThread()->Stop(); | 585 | GetCurrentThread()->Stop(); |
| @@ -524,20 +590,20 @@ static void ExitProcess() { | |||
| 524 | /// Creates a new thread | 590 | /// Creates a new thread |
| 525 | static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, VAddr stack_top, | 591 | static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, VAddr stack_top, |
| 526 | u32 priority, s32 processor_id) { | 592 | u32 priority, s32 processor_id) { |
| 527 | std::string name = fmt::format("unknown-{:X}", entry_point); | 593 | std::string name = fmt::format("thread-{:X}", entry_point); |
| 528 | 594 | ||
| 529 | if (priority > THREADPRIO_LOWEST) { | 595 | if (priority > THREADPRIO_LOWEST) { |
| 530 | return ERR_INVALID_THREAD_PRIORITY; | 596 | return ERR_INVALID_THREAD_PRIORITY; |
| 531 | } | 597 | } |
| 532 | 598 | ||
| 533 | SharedPtr<ResourceLimit>& resource_limit = Core::CurrentProcess()->resource_limit; | 599 | const ResourceLimit& resource_limit = Core::CurrentProcess()->GetResourceLimit(); |
| 534 | if (resource_limit->GetMaxResourceValue(ResourceType::Priority) > priority) { | 600 | if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { |
| 535 | return ERR_NOT_AUTHORIZED; | 601 | return ERR_NOT_AUTHORIZED; |
| 536 | } | 602 | } |
| 537 | 603 | ||
| 538 | if (processor_id == THREADPROCESSORID_DEFAULT) { | 604 | if (processor_id == THREADPROCESSORID_DEFAULT) { |
| 539 | // Set the target CPU to the one specified in the process' exheader. | 605 | // Set the target CPU to the one specified in the process' exheader. |
| 540 | processor_id = Core::CurrentProcess()->ideal_processor; | 606 | processor_id = Core::CurrentProcess()->GetDefaultProcessorID(); |
| 541 | ASSERT(processor_id != THREADPROCESSORID_DEFAULT); | 607 | ASSERT(processor_id != THREADPROCESSORID_DEFAULT); |
| 542 | } | 608 | } |
| 543 | 609 | ||
| @@ -647,16 +713,17 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target | |||
| 647 | LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x{:X}, target=0x{:08X}", | 713 | LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x{:X}, target=0x{:08X}", |
| 648 | condition_variable_addr, target); | 714 | condition_variable_addr, target); |
| 649 | 715 | ||
| 650 | auto RetrieveWaitingThreads = | 716 | auto RetrieveWaitingThreads = [](std::size_t core_index, |
| 651 | [](size_t core_index, std::vector<SharedPtr<Thread>>& waiting_threads, VAddr condvar_addr) { | 717 | std::vector<SharedPtr<Thread>>& waiting_threads, |
| 652 | const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); | 718 | VAddr condvar_addr) { |
| 653 | auto& thread_list = scheduler->GetThreadList(); | 719 | const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); |
| 720 | auto& thread_list = scheduler->GetThreadList(); | ||
| 654 | 721 | ||
| 655 | for (auto& thread : thread_list) { | 722 | for (auto& thread : thread_list) { |
| 656 | if (thread->condvar_wait_address == condvar_addr) | 723 | if (thread->condvar_wait_address == condvar_addr) |
| 657 | waiting_threads.push_back(thread); | 724 | waiting_threads.push_back(thread); |
| 658 | } | 725 | } |
| 659 | }; | 726 | }; |
| 660 | 727 | ||
| 661 | // Retrieve a list of all threads that are waiting for this condition variable. | 728 | // Retrieve a list of all threads that are waiting for this condition variable. |
| 662 | std::vector<SharedPtr<Thread>> waiting_threads; | 729 | std::vector<SharedPtr<Thread>> waiting_threads; |
| @@ -672,7 +739,7 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target | |||
| 672 | 739 | ||
| 673 | // Only process up to 'target' threads, unless 'target' is -1, in which case process | 740 | // Only process up to 'target' threads, unless 'target' is -1, in which case process |
| 674 | // them all. | 741 | // them all. |
| 675 | size_t last = waiting_threads.size(); | 742 | std::size_t last = waiting_threads.size(); |
| 676 | if (target != -1) | 743 | if (target != -1) |
| 677 | last = target; | 744 | last = target; |
| 678 | 745 | ||
| @@ -680,12 +747,12 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target | |||
| 680 | if (last > waiting_threads.size()) | 747 | if (last > waiting_threads.size()) |
| 681 | return RESULT_SUCCESS; | 748 | return RESULT_SUCCESS; |
| 682 | 749 | ||
| 683 | for (size_t index = 0; index < last; ++index) { | 750 | for (std::size_t index = 0; index < last; ++index) { |
| 684 | auto& thread = waiting_threads[index]; | 751 | auto& thread = waiting_threads[index]; |
| 685 | 752 | ||
| 686 | ASSERT(thread->condvar_wait_address == condition_variable_addr); | 753 | ASSERT(thread->condvar_wait_address == condition_variable_addr); |
| 687 | 754 | ||
| 688 | size_t current_core = Core::System::GetInstance().CurrentCoreIndex(); | 755 | std::size_t current_core = Core::System::GetInstance().CurrentCoreIndex(); |
| 689 | 756 | ||
| 690 | auto& monitor = Core::System::GetInstance().Monitor(); | 757 | auto& monitor = Core::System::GetInstance().Monitor(); |
| 691 | 758 | ||
| @@ -863,10 +930,10 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) { | |||
| 863 | } | 930 | } |
| 864 | 931 | ||
| 865 | if (core == static_cast<u32>(THREADPROCESSORID_DEFAULT)) { | 932 | if (core == static_cast<u32>(THREADPROCESSORID_DEFAULT)) { |
| 866 | ASSERT(thread->owner_process->ideal_processor != | 933 | ASSERT(thread->owner_process->GetDefaultProcessorID() != |
| 867 | static_cast<u8>(THREADPROCESSORID_DEFAULT)); | 934 | static_cast<u8>(THREADPROCESSORID_DEFAULT)); |
| 868 | // Set the target CPU to the one specified in the process' exheader. | 935 | // Set the target CPU to the one specified in the process' exheader. |
| 869 | core = thread->owner_process->ideal_processor; | 936 | core = thread->owner_process->GetDefaultProcessorID(); |
| 870 | mask = 1ull << core; | 937 | mask = 1ull << core; |
| 871 | } | 938 | } |
| 872 | 939 | ||
| @@ -898,12 +965,28 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss | |||
| 898 | LOG_TRACE(Kernel_SVC, "called, size=0x{:X}, localPerms=0x{:08X}, remotePerms=0x{:08X}", size, | 965 | LOG_TRACE(Kernel_SVC, "called, size=0x{:X}, localPerms=0x{:08X}, remotePerms=0x{:08X}", size, |
| 899 | local_permissions, remote_permissions); | 966 | local_permissions, remote_permissions); |
| 900 | 967 | ||
| 968 | // Size must be a multiple of 4KB and be less than or equal to | ||
| 969 | // approx. 8 GB (actually (1GB - 512B) * 8) | ||
| 970 | if (size == 0 || (size & 0xFFFFFFFE00000FFF) != 0) { | ||
| 971 | return ERR_INVALID_SIZE; | ||
| 972 | } | ||
| 973 | |||
| 974 | const auto local_perms = static_cast<MemoryPermission>(local_permissions); | ||
| 975 | if (local_perms != MemoryPermission::Read && local_perms != MemoryPermission::ReadWrite) { | ||
| 976 | return ERR_INVALID_MEMORY_PERMISSIONS; | ||
| 977 | } | ||
| 978 | |||
| 979 | const auto remote_perms = static_cast<MemoryPermission>(remote_permissions); | ||
| 980 | if (remote_perms != MemoryPermission::Read && remote_perms != MemoryPermission::ReadWrite && | ||
| 981 | remote_perms != MemoryPermission::DontCare) { | ||
| 982 | return ERR_INVALID_MEMORY_PERMISSIONS; | ||
| 983 | } | ||
| 984 | |||
| 901 | auto& kernel = Core::System::GetInstance().Kernel(); | 985 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 902 | auto& handle_table = kernel.HandleTable(); | 986 | auto& handle_table = kernel.HandleTable(); |
| 903 | auto shared_mem_handle = | 987 | auto shared_mem_handle = |
| 904 | SharedMemory::Create(kernel, handle_table.Get<Process>(KernelHandle::CurrentProcess), size, | 988 | SharedMemory::Create(kernel, handle_table.Get<Process>(KernelHandle::CurrentProcess), size, |
| 905 | static_cast<MemoryPermission>(local_permissions), | 989 | local_perms, remote_perms); |
| 906 | static_cast<MemoryPermission>(remote_permissions)); | ||
| 907 | 990 | ||
| 908 | CASCADE_RESULT(*handle, handle_table.Create(shared_mem_handle)); | 991 | CASCADE_RESULT(*handle, handle_table.Create(shared_mem_handle)); |
| 909 | return RESULT_SUCCESS; | 992 | return RESULT_SUCCESS; |
| @@ -977,7 +1060,7 @@ static const FunctionDef SVC_Table[] = { | |||
| 977 | {0x2B, nullptr, "FlushDataCache"}, | 1060 | {0x2B, nullptr, "FlushDataCache"}, |
| 978 | {0x2C, nullptr, "MapPhysicalMemory"}, | 1061 | {0x2C, nullptr, "MapPhysicalMemory"}, |
| 979 | {0x2D, nullptr, "UnmapPhysicalMemory"}, | 1062 | {0x2D, nullptr, "UnmapPhysicalMemory"}, |
| 980 | {0x2E, nullptr, "GetNextThreadInfo"}, | 1063 | {0x2E, nullptr, "GetFutureThreadInfo"}, |
| 981 | {0x2F, nullptr, "GetLastThreadInfo"}, | 1064 | {0x2F, nullptr, "GetLastThreadInfo"}, |
| 982 | {0x30, nullptr, "GetResourceLimitLimitValue"}, | 1065 | {0x30, nullptr, "GetResourceLimitLimitValue"}, |
| 983 | {0x31, nullptr, "GetResourceLimitCurrentValue"}, | 1066 | {0x31, nullptr, "GetResourceLimitCurrentValue"}, |
| @@ -1003,11 +1086,11 @@ static const FunctionDef SVC_Table[] = { | |||
| 1003 | {0x45, nullptr, "CreateEvent"}, | 1086 | {0x45, nullptr, "CreateEvent"}, |
| 1004 | {0x46, nullptr, "Unknown"}, | 1087 | {0x46, nullptr, "Unknown"}, |
| 1005 | {0x47, nullptr, "Unknown"}, | 1088 | {0x47, nullptr, "Unknown"}, |
| 1006 | {0x48, nullptr, "AllocateUnsafeMemory"}, | 1089 | {0x48, nullptr, "MapPhysicalMemoryUnsafe"}, |
| 1007 | {0x49, nullptr, "FreeUnsafeMemory"}, | 1090 | {0x49, nullptr, "UnmapPhysicalMemoryUnsafe"}, |
| 1008 | {0x4A, nullptr, "SetUnsafeAllocationLimit"}, | 1091 | {0x4A, nullptr, "SetUnsafeLimit"}, |
| 1009 | {0x4B, nullptr, "CreateJitMemory"}, | 1092 | {0x4B, nullptr, "CreateCodeMemory"}, |
| 1010 | {0x4C, nullptr, "MapJitMemory"}, | 1093 | {0x4C, nullptr, "ControlCodeMemory"}, |
| 1011 | {0x4D, nullptr, "SleepSystem"}, | 1094 | {0x4D, nullptr, "SleepSystem"}, |
| 1012 | {0x4E, nullptr, "ReadWriteRegister"}, | 1095 | {0x4E, nullptr, "ReadWriteRegister"}, |
| 1013 | {0x4F, nullptr, "SetProcessActivity"}, | 1096 | {0x4F, nullptr, "SetProcessActivity"}, |
| @@ -1042,7 +1125,7 @@ static const FunctionDef SVC_Table[] = { | |||
| 1042 | {0x6C, nullptr, "SetHardwareBreakPoint"}, | 1125 | {0x6C, nullptr, "SetHardwareBreakPoint"}, |
| 1043 | {0x6D, nullptr, "GetDebugThreadParam"}, | 1126 | {0x6D, nullptr, "GetDebugThreadParam"}, |
| 1044 | {0x6E, nullptr, "Unknown"}, | 1127 | {0x6E, nullptr, "Unknown"}, |
| 1045 | {0x6F, nullptr, "GetMemoryInfo"}, | 1128 | {0x6F, nullptr, "GetSystemInfo"}, |
| 1046 | {0x70, nullptr, "CreatePort"}, | 1129 | {0x70, nullptr, "CreatePort"}, |
| 1047 | {0x71, nullptr, "ManageNamedPort"}, | 1130 | {0x71, nullptr, "ManageNamedPort"}, |
| 1048 | {0x72, nullptr, "ConnectToPort"}, | 1131 | {0x72, nullptr, "ConnectToPort"}, |
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index 1eda5f879..22712e64f 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h | |||
| @@ -13,7 +13,9 @@ | |||
| 13 | 13 | ||
| 14 | namespace Kernel { | 14 | namespace Kernel { |
| 15 | 15 | ||
| 16 | #define PARAM(n) Core::CurrentArmInterface().GetReg(n) | 16 | static inline u64 Param(int n) { |
| 17 | return Core::CurrentArmInterface().GetReg(n); | ||
| 18 | } | ||
| 17 | 19 | ||
| 18 | /** | 20 | /** |
| 19 | * HLE a function return from the current ARM userland process | 21 | * HLE a function return from the current ARM userland process |
| @@ -28,23 +30,23 @@ static inline void FuncReturn(u64 res) { | |||
| 28 | 30 | ||
| 29 | template <ResultCode func(u64)> | 31 | template <ResultCode func(u64)> |
| 30 | void SvcWrap() { | 32 | void SvcWrap() { |
| 31 | FuncReturn(func(PARAM(0)).raw); | 33 | FuncReturn(func(Param(0)).raw); |
| 32 | } | 34 | } |
| 33 | 35 | ||
| 34 | template <ResultCode func(u32)> | 36 | template <ResultCode func(u32)> |
| 35 | void SvcWrap() { | 37 | void SvcWrap() { |
| 36 | FuncReturn(func((u32)PARAM(0)).raw); | 38 | FuncReturn(func((u32)Param(0)).raw); |
| 37 | } | 39 | } |
| 38 | 40 | ||
| 39 | template <ResultCode func(u32, u32)> | 41 | template <ResultCode func(u32, u32)> |
| 40 | void SvcWrap() { | 42 | void SvcWrap() { |
| 41 | FuncReturn(func((u32)PARAM(0), (u32)PARAM(1)).raw); | 43 | FuncReturn(func((u32)Param(0), (u32)Param(1)).raw); |
| 42 | } | 44 | } |
| 43 | 45 | ||
| 44 | template <ResultCode func(u32*, u32)> | 46 | template <ResultCode func(u32*, u32)> |
| 45 | void SvcWrap() { | 47 | void SvcWrap() { |
| 46 | u32 param_1 = 0; | 48 | u32 param_1 = 0; |
| 47 | u32 retval = func(¶m_1, (u32)PARAM(1)).raw; | 49 | u32 retval = func(¶m_1, (u32)Param(1)).raw; |
| 48 | Core::CurrentArmInterface().SetReg(1, param_1); | 50 | Core::CurrentArmInterface().SetReg(1, param_1); |
| 49 | FuncReturn(retval); | 51 | FuncReturn(retval); |
| 50 | } | 52 | } |
| @@ -52,39 +54,44 @@ void SvcWrap() { | |||
| 52 | template <ResultCode func(u32*, u64)> | 54 | template <ResultCode func(u32*, u64)> |
| 53 | void SvcWrap() { | 55 | void SvcWrap() { |
| 54 | u32 param_1 = 0; | 56 | u32 param_1 = 0; |
| 55 | u32 retval = func(¶m_1, PARAM(1)).raw; | 57 | u32 retval = func(¶m_1, Param(1)).raw; |
| 56 | Core::CurrentArmInterface().SetReg(1, param_1); | 58 | Core::CurrentArmInterface().SetReg(1, param_1); |
| 57 | FuncReturn(retval); | 59 | FuncReturn(retval); |
| 58 | } | 60 | } |
| 59 | 61 | ||
| 60 | template <ResultCode func(u64, s32)> | 62 | template <ResultCode func(u64, s32)> |
| 61 | void SvcWrap() { | 63 | void SvcWrap() { |
| 62 | FuncReturn(func(PARAM(0), (s32)PARAM(1)).raw); | 64 | FuncReturn(func(Param(0), (s32)Param(1)).raw); |
| 65 | } | ||
| 66 | |||
| 67 | template <ResultCode func(u64, u32)> | ||
| 68 | void SvcWrap() { | ||
| 69 | FuncReturn(func(Param(0), static_cast<u32>(Param(1))).raw); | ||
| 63 | } | 70 | } |
| 64 | 71 | ||
| 65 | template <ResultCode func(u64*, u64)> | 72 | template <ResultCode func(u64*, u64)> |
| 66 | void SvcWrap() { | 73 | void SvcWrap() { |
| 67 | u64 param_1 = 0; | 74 | u64 param_1 = 0; |
| 68 | u32 retval = func(¶m_1, PARAM(1)).raw; | 75 | u32 retval = func(¶m_1, Param(1)).raw; |
| 69 | Core::CurrentArmInterface().SetReg(1, param_1); | 76 | Core::CurrentArmInterface().SetReg(1, param_1); |
| 70 | FuncReturn(retval); | 77 | FuncReturn(retval); |
| 71 | } | 78 | } |
| 72 | 79 | ||
| 73 | template <ResultCode func(u32, u64)> | 80 | template <ResultCode func(u32, u64)> |
| 74 | void SvcWrap() { | 81 | void SvcWrap() { |
| 75 | FuncReturn(func((u32)(PARAM(0) & 0xFFFFFFFF), PARAM(1)).raw); | 82 | FuncReturn(func((u32)(Param(0) & 0xFFFFFFFF), Param(1)).raw); |
| 76 | } | 83 | } |
| 77 | 84 | ||
| 78 | template <ResultCode func(u32, u32, u64)> | 85 | template <ResultCode func(u32, u32, u64)> |
| 79 | void SvcWrap() { | 86 | void SvcWrap() { |
| 80 | FuncReturn(func((u32)(PARAM(0) & 0xFFFFFFFF), (u32)(PARAM(1) & 0xFFFFFFFF), PARAM(2)).raw); | 87 | FuncReturn(func((u32)(Param(0) & 0xFFFFFFFF), (u32)(Param(1) & 0xFFFFFFFF), Param(2)).raw); |
| 81 | } | 88 | } |
| 82 | 89 | ||
| 83 | template <ResultCode func(u32, u32*, u64*)> | 90 | template <ResultCode func(u32, u32*, u64*)> |
| 84 | void SvcWrap() { | 91 | void SvcWrap() { |
| 85 | u32 param_1 = 0; | 92 | u32 param_1 = 0; |
| 86 | u64 param_2 = 0; | 93 | u64 param_2 = 0; |
| 87 | ResultCode retval = func((u32)(PARAM(2) & 0xFFFFFFFF), ¶m_1, ¶m_2); | 94 | ResultCode retval = func((u32)(Param(2) & 0xFFFFFFFF), ¶m_1, ¶m_2); |
| 88 | Core::CurrentArmInterface().SetReg(1, param_1); | 95 | Core::CurrentArmInterface().SetReg(1, param_1); |
| 89 | Core::CurrentArmInterface().SetReg(2, param_2); | 96 | Core::CurrentArmInterface().SetReg(2, param_2); |
| 90 | FuncReturn(retval.raw); | 97 | FuncReturn(retval.raw); |
| @@ -93,46 +100,46 @@ void SvcWrap() { | |||
| 93 | template <ResultCode func(u64, u64, u32, u32)> | 100 | template <ResultCode func(u64, u64, u32, u32)> |
| 94 | void SvcWrap() { | 101 | void SvcWrap() { |
| 95 | FuncReturn( | 102 | FuncReturn( |
| 96 | func(PARAM(0), PARAM(1), (u32)(PARAM(3) & 0xFFFFFFFF), (u32)(PARAM(3) & 0xFFFFFFFF)).raw); | 103 | func(Param(0), Param(1), (u32)(Param(3) & 0xFFFFFFFF), (u32)(Param(3) & 0xFFFFFFFF)).raw); |
| 97 | } | 104 | } |
| 98 | 105 | ||
| 99 | template <ResultCode func(u32, u64, u32)> | 106 | template <ResultCode func(u32, u64, u32)> |
| 100 | void SvcWrap() { | 107 | void SvcWrap() { |
| 101 | FuncReturn(func((u32)PARAM(0), PARAM(1), (u32)PARAM(2)).raw); | 108 | FuncReturn(func((u32)Param(0), Param(1), (u32)Param(2)).raw); |
| 102 | } | 109 | } |
| 103 | 110 | ||
| 104 | template <ResultCode func(u64, u64, u64)> | 111 | template <ResultCode func(u64, u64, u64)> |
| 105 | void SvcWrap() { | 112 | void SvcWrap() { |
| 106 | FuncReturn(func(PARAM(0), PARAM(1), PARAM(2)).raw); | 113 | FuncReturn(func(Param(0), Param(1), Param(2)).raw); |
| 107 | } | 114 | } |
| 108 | 115 | ||
| 109 | template <ResultCode func(u32, u64, u64, u32)> | 116 | template <ResultCode func(u32, u64, u64, u32)> |
| 110 | void SvcWrap() { | 117 | void SvcWrap() { |
| 111 | FuncReturn(func((u32)PARAM(0), PARAM(1), PARAM(2), (u32)PARAM(3)).raw); | 118 | FuncReturn(func((u32)Param(0), Param(1), Param(2), (u32)Param(3)).raw); |
| 112 | } | 119 | } |
| 113 | 120 | ||
| 114 | template <ResultCode func(u32, u64, u64)> | 121 | template <ResultCode func(u32, u64, u64)> |
| 115 | void SvcWrap() { | 122 | void SvcWrap() { |
| 116 | FuncReturn(func((u32)PARAM(0), PARAM(1), PARAM(2)).raw); | 123 | FuncReturn(func((u32)Param(0), Param(1), Param(2)).raw); |
| 117 | } | 124 | } |
| 118 | 125 | ||
| 119 | template <ResultCode func(u32*, u64, u64, s64)> | 126 | template <ResultCode func(u32*, u64, u64, s64)> |
| 120 | void SvcWrap() { | 127 | void SvcWrap() { |
| 121 | u32 param_1 = 0; | 128 | u32 param_1 = 0; |
| 122 | ResultCode retval = func(¶m_1, PARAM(1), (u32)(PARAM(2) & 0xFFFFFFFF), (s64)PARAM(3)); | 129 | ResultCode retval = func(¶m_1, Param(1), (u32)(Param(2) & 0xFFFFFFFF), (s64)Param(3)); |
| 123 | Core::CurrentArmInterface().SetReg(1, param_1); | 130 | Core::CurrentArmInterface().SetReg(1, param_1); |
| 124 | FuncReturn(retval.raw); | 131 | FuncReturn(retval.raw); |
| 125 | } | 132 | } |
| 126 | 133 | ||
| 127 | template <ResultCode func(u64, u64, u32, s64)> | 134 | template <ResultCode func(u64, u64, u32, s64)> |
| 128 | void SvcWrap() { | 135 | void SvcWrap() { |
| 129 | FuncReturn(func(PARAM(0), PARAM(1), (u32)PARAM(2), (s64)PARAM(3)).raw); | 136 | FuncReturn(func(Param(0), Param(1), (u32)Param(2), (s64)Param(3)).raw); |
| 130 | } | 137 | } |
| 131 | 138 | ||
| 132 | template <ResultCode func(u64*, u64, u64, u64)> | 139 | template <ResultCode func(u64*, u64, u64, u64)> |
| 133 | void SvcWrap() { | 140 | void SvcWrap() { |
| 134 | u64 param_1 = 0; | 141 | u64 param_1 = 0; |
| 135 | u32 retval = func(¶m_1, PARAM(1), PARAM(2), PARAM(3)).raw; | 142 | u32 retval = func(¶m_1, Param(1), Param(2), Param(3)).raw; |
| 136 | Core::CurrentArmInterface().SetReg(1, param_1); | 143 | Core::CurrentArmInterface().SetReg(1, param_1); |
| 137 | FuncReturn(retval); | 144 | FuncReturn(retval); |
| 138 | } | 145 | } |
| @@ -141,7 +148,7 @@ template <ResultCode func(u32*, u64, u64, u64, u32, s32)> | |||
| 141 | void SvcWrap() { | 148 | void SvcWrap() { |
| 142 | u32 param_1 = 0; | 149 | u32 param_1 = 0; |
| 143 | u32 retval = | 150 | u32 retval = |
| 144 | func(¶m_1, PARAM(1), PARAM(2), PARAM(3), (u32)PARAM(4), (s32)(PARAM(5) & 0xFFFFFFFF)) | 151 | func(¶m_1, Param(1), Param(2), Param(3), (u32)Param(4), (s32)(Param(5) & 0xFFFFFFFF)) |
| 145 | .raw; | 152 | .raw; |
| 146 | Core::CurrentArmInterface().SetReg(1, param_1); | 153 | Core::CurrentArmInterface().SetReg(1, param_1); |
| 147 | FuncReturn(retval); | 154 | FuncReturn(retval); |
| @@ -151,13 +158,13 @@ template <ResultCode func(MemoryInfo*, PageInfo*, u64)> | |||
| 151 | void SvcWrap() { | 158 | void SvcWrap() { |
| 152 | MemoryInfo memory_info = {}; | 159 | MemoryInfo memory_info = {}; |
| 153 | PageInfo page_info = {}; | 160 | PageInfo page_info = {}; |
| 154 | u32 retval = func(&memory_info, &page_info, PARAM(2)).raw; | 161 | u32 retval = func(&memory_info, &page_info, Param(2)).raw; |
| 155 | 162 | ||
| 156 | Memory::Write64(PARAM(0), memory_info.base_address); | 163 | Memory::Write64(Param(0), memory_info.base_address); |
| 157 | Memory::Write64(PARAM(0) + 8, memory_info.size); | 164 | Memory::Write64(Param(0) + 8, memory_info.size); |
| 158 | Memory::Write32(PARAM(0) + 16, memory_info.type); | 165 | Memory::Write32(Param(0) + 16, memory_info.type); |
| 159 | Memory::Write32(PARAM(0) + 20, memory_info.attributes); | 166 | Memory::Write32(Param(0) + 20, memory_info.attributes); |
| 160 | Memory::Write32(PARAM(0) + 24, memory_info.permission); | 167 | Memory::Write32(Param(0) + 24, memory_info.permission); |
| 161 | 168 | ||
| 162 | FuncReturn(retval); | 169 | FuncReturn(retval); |
| 163 | } | 170 | } |
| @@ -165,7 +172,7 @@ void SvcWrap() { | |||
| 165 | template <ResultCode func(u32*, u64, u64, u32)> | 172 | template <ResultCode func(u32*, u64, u64, u32)> |
| 166 | void SvcWrap() { | 173 | void SvcWrap() { |
| 167 | u32 param_1 = 0; | 174 | u32 param_1 = 0; |
| 168 | u32 retval = func(¶m_1, PARAM(1), PARAM(2), (u32)(PARAM(3) & 0xFFFFFFFF)).raw; | 175 | u32 retval = func(¶m_1, Param(1), Param(2), (u32)(Param(3) & 0xFFFFFFFF)).raw; |
| 169 | Core::CurrentArmInterface().SetReg(1, param_1); | 176 | Core::CurrentArmInterface().SetReg(1, param_1); |
| 170 | FuncReturn(retval); | 177 | FuncReturn(retval); |
| 171 | } | 178 | } |
| @@ -174,7 +181,7 @@ template <ResultCode func(Handle*, u64, u32, u32)> | |||
| 174 | void SvcWrap() { | 181 | void SvcWrap() { |
| 175 | u32 param_1 = 0; | 182 | u32 param_1 = 0; |
| 176 | u32 retval = | 183 | u32 retval = |
| 177 | func(¶m_1, PARAM(1), (u32)(PARAM(2) & 0xFFFFFFFF), (u32)(PARAM(3) & 0xFFFFFFFF)).raw; | 184 | func(¶m_1, Param(1), (u32)(Param(2) & 0xFFFFFFFF), (u32)(Param(3) & 0xFFFFFFFF)).raw; |
| 178 | Core::CurrentArmInterface().SetReg(1, param_1); | 185 | Core::CurrentArmInterface().SetReg(1, param_1); |
| 179 | FuncReturn(retval); | 186 | FuncReturn(retval); |
| 180 | } | 187 | } |
| @@ -182,14 +189,14 @@ void SvcWrap() { | |||
| 182 | template <ResultCode func(u64, u32, s32, s64)> | 189 | template <ResultCode func(u64, u32, s32, s64)> |
| 183 | void SvcWrap() { | 190 | void SvcWrap() { |
| 184 | FuncReturn( | 191 | FuncReturn( |
| 185 | func(PARAM(0), (u32)(PARAM(1) & 0xFFFFFFFF), (s32)(PARAM(2) & 0xFFFFFFFF), (s64)PARAM(3)) | 192 | func(Param(0), (u32)(Param(1) & 0xFFFFFFFF), (s32)(Param(2) & 0xFFFFFFFF), (s64)Param(3)) |
| 186 | .raw); | 193 | .raw); |
| 187 | } | 194 | } |
| 188 | 195 | ||
| 189 | template <ResultCode func(u64, u32, s32, s32)> | 196 | template <ResultCode func(u64, u32, s32, s32)> |
| 190 | void SvcWrap() { | 197 | void SvcWrap() { |
| 191 | FuncReturn(func(PARAM(0), (u32)(PARAM(1) & 0xFFFFFFFF), (s32)(PARAM(2) & 0xFFFFFFFF), | 198 | FuncReturn(func(Param(0), (u32)(Param(1) & 0xFFFFFFFF), (s32)(Param(2) & 0xFFFFFFFF), |
| 192 | (s32)(PARAM(3) & 0xFFFFFFFF)) | 199 | (s32)(Param(3) & 0xFFFFFFFF)) |
| 193 | .raw); | 200 | .raw); |
| 194 | } | 201 | } |
| 195 | 202 | ||
| @@ -219,20 +226,17 @@ void SvcWrap() { | |||
| 219 | 226 | ||
| 220 | template <void func(s64)> | 227 | template <void func(s64)> |
| 221 | void SvcWrap() { | 228 | void SvcWrap() { |
| 222 | func((s64)PARAM(0)); | 229 | func((s64)Param(0)); |
| 223 | } | 230 | } |
| 224 | 231 | ||
| 225 | template <void func(u64, u64 len)> | 232 | template <void func(u64, u64 len)> |
| 226 | void SvcWrap() { | 233 | void SvcWrap() { |
| 227 | func(PARAM(0), PARAM(1)); | 234 | func(Param(0), Param(1)); |
| 228 | } | 235 | } |
| 229 | 236 | ||
| 230 | template <void func(u64, u64, u64)> | 237 | template <void func(u64, u64, u64)> |
| 231 | void SvcWrap() { | 238 | void SvcWrap() { |
| 232 | func(PARAM(0), PARAM(1), PARAM(2)); | 239 | func(Param(0), Param(1), Param(2)); |
| 233 | } | 240 | } |
| 234 | 241 | ||
| 235 | #undef PARAM | ||
| 236 | #undef FuncReturn | ||
| 237 | |||
| 238 | } // namespace Kernel | 242 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 3f12a84dc..b5c16cfbb 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -65,10 +65,7 @@ void Thread::Stop() { | |||
| 65 | wait_objects.clear(); | 65 | wait_objects.clear(); |
| 66 | 66 | ||
| 67 | // Mark the TLS slot in the thread's page as free. | 67 | // Mark the TLS slot in the thread's page as free. |
| 68 | const u64 tls_page = (tls_address - Memory::TLS_AREA_VADDR) / Memory::PAGE_SIZE; | 68 | owner_process->FreeTLSSlot(tls_address); |
| 69 | const u64 tls_slot = | ||
| 70 | ((tls_address - Memory::TLS_AREA_VADDR) % Memory::PAGE_SIZE) / Memory::TLS_ENTRY_SIZE; | ||
| 71 | Core::CurrentProcess()->tls_slots[tls_page].reset(tls_slot); | ||
| 72 | } | 69 | } |
| 73 | 70 | ||
| 74 | void WaitCurrentThread_Sleep() { | 71 | void WaitCurrentThread_Sleep() { |
| @@ -178,32 +175,6 @@ void Thread::ResumeFromWait() { | |||
| 178 | } | 175 | } |
| 179 | 176 | ||
| 180 | /** | 177 | /** |
| 181 | * Finds a free location for the TLS section of a thread. | ||
| 182 | * @param tls_slots The TLS page array of the thread's owner process. | ||
| 183 | * Returns a tuple of (page, slot, alloc_needed) where: | ||
| 184 | * page: The index of the first allocated TLS page that has free slots. | ||
| 185 | * slot: The index of the first free slot in the indicated page. | ||
| 186 | * alloc_needed: Whether there's a need to allocate a new TLS page (All pages are full). | ||
| 187 | */ | ||
| 188 | static std::tuple<std::size_t, std::size_t, bool> GetFreeThreadLocalSlot( | ||
| 189 | const std::vector<std::bitset<8>>& tls_slots) { | ||
| 190 | // Iterate over all the allocated pages, and try to find one where not all slots are used. | ||
| 191 | for (std::size_t page = 0; page < tls_slots.size(); ++page) { | ||
| 192 | const auto& page_tls_slots = tls_slots[page]; | ||
| 193 | if (!page_tls_slots.all()) { | ||
| 194 | // We found a page with at least one free slot, find which slot it is | ||
| 195 | for (std::size_t slot = 0; slot < page_tls_slots.size(); ++slot) { | ||
| 196 | if (!page_tls_slots.test(slot)) { | ||
| 197 | return std::make_tuple(page, slot, false); | ||
| 198 | } | ||
| 199 | } | ||
| 200 | } | ||
| 201 | } | ||
| 202 | |||
| 203 | return std::make_tuple(0, 0, true); | ||
| 204 | } | ||
| 205 | |||
| 206 | /** | ||
| 207 | * Resets a thread context, making it ready to be scheduled and run by the CPU | 178 | * Resets a thread context, making it ready to be scheduled and run by the CPU |
| 208 | * @param context Thread context to reset | 179 | * @param context Thread context to reset |
| 209 | * @param stack_top Address of the top of the stack | 180 | * @param stack_top Address of the top of the stack |
| @@ -217,8 +188,8 @@ static void ResetThreadContext(Core::ARM_Interface::ThreadContext& context, VAdd | |||
| 217 | context.cpu_registers[0] = arg; | 188 | context.cpu_registers[0] = arg; |
| 218 | context.pc = entry_point; | 189 | context.pc = entry_point; |
| 219 | context.sp = stack_top; | 190 | context.sp = stack_top; |
| 220 | context.cpsr = 0; | 191 | context.pstate = 0; |
| 221 | context.fpscr = 0; | 192 | context.fpcr = 0; |
| 222 | } | 193 | } |
| 223 | 194 | ||
| 224 | ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name, VAddr entry_point, | 195 | ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name, VAddr entry_point, |
| @@ -264,32 +235,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name | |||
| 264 | thread->owner_process = owner_process; | 235 | thread->owner_process = owner_process; |
| 265 | thread->scheduler = Core::System::GetInstance().Scheduler(processor_id); | 236 | thread->scheduler = Core::System::GetInstance().Scheduler(processor_id); |
| 266 | thread->scheduler->AddThread(thread, priority); | 237 | thread->scheduler->AddThread(thread, priority); |
| 267 | 238 | thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread); | |
| 268 | // Find the next available TLS index, and mark it as used | ||
| 269 | auto& tls_slots = owner_process->tls_slots; | ||
| 270 | |||
| 271 | auto [available_page, available_slot, needs_allocation] = GetFreeThreadLocalSlot(tls_slots); | ||
| 272 | if (needs_allocation) { | ||
| 273 | tls_slots.emplace_back(0); // The page is completely available at the start | ||
| 274 | available_page = tls_slots.size() - 1; | ||
| 275 | available_slot = 0; // Use the first slot in the new page | ||
| 276 | |||
| 277 | // Allocate some memory from the end of the linear heap for this region. | ||
| 278 | const size_t offset = thread->tls_memory->size(); | ||
| 279 | thread->tls_memory->insert(thread->tls_memory->end(), Memory::PAGE_SIZE, 0); | ||
| 280 | |||
| 281 | auto& vm_manager = owner_process->vm_manager; | ||
| 282 | vm_manager.RefreshMemoryBlockMappings(thread->tls_memory.get()); | ||
| 283 | |||
| 284 | vm_manager.MapMemoryBlock(Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE, | ||
| 285 | thread->tls_memory, 0, Memory::PAGE_SIZE, | ||
| 286 | MemoryState::ThreadLocal); | ||
| 287 | } | ||
| 288 | |||
| 289 | // Mark the slot as used | ||
| 290 | tls_slots[available_page].set(available_slot); | ||
| 291 | thread->tls_address = Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE + | ||
| 292 | available_slot * Memory::TLS_ENTRY_SIZE; | ||
| 293 | 239 | ||
| 294 | // TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used | 240 | // TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used |
| 295 | // to initialize the context | 241 | // to initialize the context |
| @@ -311,13 +257,14 @@ void Thread::BoostPriority(u32 priority) { | |||
| 311 | } | 257 | } |
| 312 | 258 | ||
| 313 | SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 priority, | 259 | SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 priority, |
| 314 | SharedPtr<Process> owner_process) { | 260 | Process& owner_process) { |
| 315 | // Setup page table so we can write to memory | 261 | // Setup page table so we can write to memory |
| 316 | SetCurrentPageTable(&Core::CurrentProcess()->vm_manager.page_table); | 262 | SetCurrentPageTable(&owner_process.VMManager().page_table); |
| 317 | 263 | ||
| 318 | // Initialize new "main" thread | 264 | // Initialize new "main" thread |
| 265 | const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress(); | ||
| 319 | auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, THREADPROCESSORID_0, | 266 | auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, THREADPROCESSORID_0, |
| 320 | Memory::STACK_AREA_VADDR_END, std::move(owner_process)); | 267 | stack_top, &owner_process); |
| 321 | 268 | ||
| 322 | SharedPtr<Thread> thread = std::move(thread_res).Unwrap(); | 269 | SharedPtr<Thread> thread = std::move(thread_res).Unwrap(); |
| 323 | 270 | ||
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index cb57ee78a..4250144c3 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h | |||
| @@ -62,6 +62,9 @@ enum class ThreadWakeupReason { | |||
| 62 | 62 | ||
| 63 | class Thread final : public WaitObject { | 63 | class Thread final : public WaitObject { |
| 64 | public: | 64 | public: |
| 65 | using TLSMemory = std::vector<u8>; | ||
| 66 | using TLSMemoryPtr = std::shared_ptr<TLSMemory>; | ||
| 67 | |||
| 65 | /** | 68 | /** |
| 66 | * Creates and returns a new thread. The new thread is immediately scheduled | 69 | * Creates and returns a new thread. The new thread is immediately scheduled |
| 67 | * @param kernel The kernel instance this thread will be created under. | 70 | * @param kernel The kernel instance this thread will be created under. |
| @@ -134,6 +137,14 @@ public: | |||
| 134 | return thread_id; | 137 | return thread_id; |
| 135 | } | 138 | } |
| 136 | 139 | ||
| 140 | TLSMemoryPtr& GetTLSMemory() { | ||
| 141 | return tls_memory; | ||
| 142 | } | ||
| 143 | |||
| 144 | const TLSMemoryPtr& GetTLSMemory() const { | ||
| 145 | return tls_memory; | ||
| 146 | } | ||
| 147 | |||
| 137 | /** | 148 | /** |
| 138 | * Resumes a thread from waiting | 149 | * Resumes a thread from waiting |
| 139 | */ | 150 | */ |
| @@ -254,7 +265,7 @@ public: | |||
| 254 | Handle callback_handle; | 265 | Handle callback_handle; |
| 255 | 266 | ||
| 256 | using WakeupCallback = bool(ThreadWakeupReason reason, SharedPtr<Thread> thread, | 267 | using WakeupCallback = bool(ThreadWakeupReason reason, SharedPtr<Thread> thread, |
| 257 | SharedPtr<WaitObject> object, size_t index); | 268 | SharedPtr<WaitObject> object, std::size_t index); |
| 258 | // Callback that will be invoked when the thread is resumed from a waiting state. If the thread | 269 | // Callback that will be invoked when the thread is resumed from a waiting state. If the thread |
| 259 | // was waiting via WaitSynchronizationN then the object will be the last object that became | 270 | // was waiting via WaitSynchronizationN then the object will be the last object that became |
| 260 | // available. In case of a timeout, the object will be nullptr. | 271 | // available. In case of a timeout, the object will be nullptr. |
| @@ -269,7 +280,7 @@ private: | |||
| 269 | explicit Thread(KernelCore& kernel); | 280 | explicit Thread(KernelCore& kernel); |
| 270 | ~Thread() override; | 281 | ~Thread() override; |
| 271 | 282 | ||
| 272 | std::shared_ptr<std::vector<u8>> tls_memory = std::make_shared<std::vector<u8>>(); | 283 | TLSMemoryPtr tls_memory = std::make_shared<TLSMemory>(); |
| 273 | }; | 284 | }; |
| 274 | 285 | ||
| 275 | /** | 286 | /** |
| @@ -281,7 +292,7 @@ private: | |||
| 281 | * @return A shared pointer to the main thread | 292 | * @return A shared pointer to the main thread |
| 282 | */ | 293 | */ |
| 283 | SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 priority, | 294 | SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 priority, |
| 284 | SharedPtr<Process> owner_process); | 295 | Process& owner_process); |
| 285 | 296 | ||
| 286 | /** | 297 | /** |
| 287 | * Gets the current thread | 298 | * Gets the current thread |
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index 479cacb62..e412309fd 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 10 | #include "core/arm/arm_interface.h" | 10 | #include "core/arm/arm_interface.h" |
| 11 | #include "core/core.h" | 11 | #include "core/core.h" |
| 12 | #include "core/file_sys/program_metadata.h" | ||
| 12 | #include "core/hle/kernel/errors.h" | 13 | #include "core/hle/kernel/errors.h" |
| 13 | #include "core/hle/kernel/vm_manager.h" | 14 | #include "core/hle/kernel/vm_manager.h" |
| 14 | #include "core/memory.h" | 15 | #include "core/memory.h" |
| @@ -54,30 +55,32 @@ bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const { | |||
| 54 | } | 55 | } |
| 55 | 56 | ||
| 56 | VMManager::VMManager() { | 57 | VMManager::VMManager() { |
| 57 | Reset(); | 58 | // Default to assuming a 39-bit address space. This way we have a sane |
| 59 | // starting point with executables that don't provide metadata. | ||
| 60 | Reset(FileSys::ProgramAddressSpaceType::Is39Bit); | ||
| 58 | } | 61 | } |
| 59 | 62 | ||
| 60 | VMManager::~VMManager() { | 63 | VMManager::~VMManager() { |
| 61 | Reset(); | 64 | Reset(FileSys::ProgramAddressSpaceType::Is39Bit); |
| 62 | } | 65 | } |
| 63 | 66 | ||
| 64 | void VMManager::Reset() { | 67 | void VMManager::Reset(FileSys::ProgramAddressSpaceType type) { |
| 65 | vma_map.clear(); | 68 | Clear(); |
| 69 | |||
| 70 | InitializeMemoryRegionRanges(type); | ||
| 71 | |||
| 72 | page_table.Resize(address_space_width); | ||
| 66 | 73 | ||
| 67 | // Initialize the map with a single free region covering the entire managed space. | 74 | // Initialize the map with a single free region covering the entire managed space. |
| 68 | VirtualMemoryArea initial_vma; | 75 | VirtualMemoryArea initial_vma; |
| 69 | initial_vma.size = MAX_ADDRESS; | 76 | initial_vma.size = address_space_end; |
| 70 | vma_map.emplace(initial_vma.base, initial_vma); | 77 | vma_map.emplace(initial_vma.base, initial_vma); |
| 71 | 78 | ||
| 72 | page_table.pointers.fill(nullptr); | ||
| 73 | page_table.special_regions.clear(); | ||
| 74 | page_table.attributes.fill(Memory::PageType::Unmapped); | ||
| 75 | |||
| 76 | UpdatePageTableForVMA(initial_vma); | 79 | UpdatePageTableForVMA(initial_vma); |
| 77 | } | 80 | } |
| 78 | 81 | ||
| 79 | VMManager::VMAHandle VMManager::FindVMA(VAddr target) const { | 82 | VMManager::VMAHandle VMManager::FindVMA(VAddr target) const { |
| 80 | if (target >= MAX_ADDRESS) { | 83 | if (target >= address_space_end) { |
| 81 | return vma_map.end(); | 84 | return vma_map.end(); |
| 82 | } else { | 85 | } else { |
| 83 | return std::prev(vma_map.upper_bound(target)); | 86 | return std::prev(vma_map.upper_bound(target)); |
| @@ -86,7 +89,7 @@ VMManager::VMAHandle VMManager::FindVMA(VAddr target) const { | |||
| 86 | 89 | ||
| 87 | ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target, | 90 | ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target, |
| 88 | std::shared_ptr<std::vector<u8>> block, | 91 | std::shared_ptr<std::vector<u8>> block, |
| 89 | size_t offset, u64 size, | 92 | std::size_t offset, u64 size, |
| 90 | MemoryState state) { | 93 | MemoryState state) { |
| 91 | ASSERT(block != nullptr); | 94 | ASSERT(block != nullptr); |
| 92 | ASSERT(offset + size <= block->size()); | 95 | ASSERT(offset + size <= block->size()); |
| @@ -291,7 +294,7 @@ ResultVal<VMManager::VMAIter> VMManager::CarveVMARange(VAddr target, u64 size) { | |||
| 291 | 294 | ||
| 292 | const VAddr target_end = target + size; | 295 | const VAddr target_end = target + size; |
| 293 | ASSERT(target_end >= target); | 296 | ASSERT(target_end >= target); |
| 294 | ASSERT(target_end <= MAX_ADDRESS); | 297 | ASSERT(target_end <= address_space_end); |
| 295 | ASSERT(size > 0); | 298 | ASSERT(size > 0); |
| 296 | 299 | ||
| 297 | VMAIter begin_vma = StripIterConstness(FindVMA(target)); | 300 | VMAIter begin_vma = StripIterConstness(FindVMA(target)); |
| @@ -382,6 +385,85 @@ void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) { | |||
| 382 | } | 385 | } |
| 383 | } | 386 | } |
| 384 | 387 | ||
| 388 | void VMManager::InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType type) { | ||
| 389 | u64 map_region_size = 0; | ||
| 390 | u64 heap_region_size = 0; | ||
| 391 | u64 new_map_region_size = 0; | ||
| 392 | u64 tls_io_region_size = 0; | ||
| 393 | |||
| 394 | switch (type) { | ||
| 395 | case FileSys::ProgramAddressSpaceType::Is32Bit: | ||
| 396 | address_space_width = 32; | ||
| 397 | code_region_base = 0x200000; | ||
| 398 | code_region_end = code_region_base + 0x3FE00000; | ||
| 399 | map_region_size = 0x40000000; | ||
| 400 | heap_region_size = 0x40000000; | ||
| 401 | break; | ||
| 402 | case FileSys::ProgramAddressSpaceType::Is36Bit: | ||
| 403 | address_space_width = 36; | ||
| 404 | code_region_base = 0x8000000; | ||
| 405 | code_region_end = code_region_base + 0x78000000; | ||
| 406 | map_region_size = 0x180000000; | ||
| 407 | heap_region_size = 0x180000000; | ||
| 408 | break; | ||
| 409 | case FileSys::ProgramAddressSpaceType::Is32BitNoMap: | ||
| 410 | address_space_width = 32; | ||
| 411 | code_region_base = 0x200000; | ||
| 412 | code_region_end = code_region_base + 0x3FE00000; | ||
| 413 | map_region_size = 0; | ||
| 414 | heap_region_size = 0x80000000; | ||
| 415 | break; | ||
| 416 | case FileSys::ProgramAddressSpaceType::Is39Bit: | ||
| 417 | address_space_width = 39; | ||
| 418 | code_region_base = 0x8000000; | ||
| 419 | code_region_end = code_region_base + 0x80000000; | ||
| 420 | map_region_size = 0x1000000000; | ||
| 421 | heap_region_size = 0x180000000; | ||
| 422 | new_map_region_size = 0x80000000; | ||
| 423 | tls_io_region_size = 0x1000000000; | ||
| 424 | break; | ||
| 425 | default: | ||
| 426 | UNREACHABLE_MSG("Invalid address space type specified: {}", static_cast<u32>(type)); | ||
| 427 | return; | ||
| 428 | } | ||
| 429 | |||
| 430 | address_space_base = 0; | ||
| 431 | address_space_end = 1ULL << address_space_width; | ||
| 432 | |||
| 433 | map_region_base = code_region_end; | ||
| 434 | map_region_end = map_region_base + map_region_size; | ||
| 435 | |||
| 436 | heap_region_base = map_region_end; | ||
| 437 | heap_region_end = heap_region_base + heap_region_size; | ||
| 438 | |||
| 439 | new_map_region_base = heap_region_end; | ||
| 440 | new_map_region_end = new_map_region_base + new_map_region_size; | ||
| 441 | |||
| 442 | tls_io_region_base = new_map_region_end; | ||
| 443 | tls_io_region_end = tls_io_region_base + tls_io_region_size; | ||
| 444 | |||
| 445 | if (new_map_region_size == 0) { | ||
| 446 | new_map_region_base = address_space_base; | ||
| 447 | new_map_region_end = address_space_end; | ||
| 448 | } | ||
| 449 | } | ||
| 450 | |||
| 451 | void VMManager::Clear() { | ||
| 452 | ClearVMAMap(); | ||
| 453 | ClearPageTable(); | ||
| 454 | } | ||
| 455 | |||
| 456 | void VMManager::ClearVMAMap() { | ||
| 457 | vma_map.clear(); | ||
| 458 | } | ||
| 459 | |||
| 460 | void VMManager::ClearPageTable() { | ||
| 461 | std::fill(page_table.pointers.begin(), page_table.pointers.end(), nullptr); | ||
| 462 | page_table.special_regions.clear(); | ||
| 463 | std::fill(page_table.attributes.begin(), page_table.attributes.end(), | ||
| 464 | Memory::PageType::Unmapped); | ||
| 465 | } | ||
| 466 | |||
| 385 | u64 VMManager::GetTotalMemoryUsage() const { | 467 | u64 VMManager::GetTotalMemoryUsage() const { |
| 386 | LOG_WARNING(Kernel, "(STUBBED) called"); | 468 | LOG_WARNING(Kernel, "(STUBBED) called"); |
| 387 | return 0xF8000000; | 469 | return 0xF8000000; |
| @@ -392,14 +474,80 @@ u64 VMManager::GetTotalHeapUsage() const { | |||
| 392 | return 0x0; | 474 | return 0x0; |
| 393 | } | 475 | } |
| 394 | 476 | ||
| 395 | VAddr VMManager::GetAddressSpaceBaseAddr() const { | 477 | VAddr VMManager::GetAddressSpaceBaseAddress() const { |
| 396 | LOG_WARNING(Kernel, "(STUBBED) called"); | 478 | return address_space_base; |
| 397 | return 0x8000000; | 479 | } |
| 480 | |||
| 481 | VAddr VMManager::GetAddressSpaceEndAddress() const { | ||
| 482 | return address_space_end; | ||
| 398 | } | 483 | } |
| 399 | 484 | ||
| 400 | u64 VMManager::GetAddressSpaceSize() const { | 485 | u64 VMManager::GetAddressSpaceSize() const { |
| 401 | LOG_WARNING(Kernel, "(STUBBED) called"); | 486 | return address_space_end - address_space_base; |
| 402 | return MAX_ADDRESS; | 487 | } |
| 488 | |||
| 489 | u64 VMManager::GetAddressSpaceWidth() const { | ||
| 490 | return address_space_width; | ||
| 491 | } | ||
| 492 | |||
| 493 | VAddr VMManager::GetCodeRegionBaseAddress() const { | ||
| 494 | return code_region_base; | ||
| 495 | } | ||
| 496 | |||
| 497 | VAddr VMManager::GetCodeRegionEndAddress() const { | ||
| 498 | return code_region_end; | ||
| 499 | } | ||
| 500 | |||
| 501 | u64 VMManager::GetCodeRegionSize() const { | ||
| 502 | return code_region_end - code_region_base; | ||
| 503 | } | ||
| 504 | |||
| 505 | VAddr VMManager::GetHeapRegionBaseAddress() const { | ||
| 506 | return heap_region_base; | ||
| 507 | } | ||
| 508 | |||
| 509 | VAddr VMManager::GetHeapRegionEndAddress() const { | ||
| 510 | return heap_region_end; | ||
| 511 | } | ||
| 512 | |||
| 513 | u64 VMManager::GetHeapRegionSize() const { | ||
| 514 | return heap_region_end - heap_region_base; | ||
| 515 | } | ||
| 516 | |||
| 517 | VAddr VMManager::GetMapRegionBaseAddress() const { | ||
| 518 | return map_region_base; | ||
| 519 | } | ||
| 520 | |||
| 521 | VAddr VMManager::GetMapRegionEndAddress() const { | ||
| 522 | return map_region_end; | ||
| 523 | } | ||
| 524 | |||
| 525 | u64 VMManager::GetMapRegionSize() const { | ||
| 526 | return map_region_end - map_region_base; | ||
| 527 | } | ||
| 528 | |||
| 529 | VAddr VMManager::GetNewMapRegionBaseAddress() const { | ||
| 530 | return new_map_region_base; | ||
| 531 | } | ||
| 532 | |||
| 533 | VAddr VMManager::GetNewMapRegionEndAddress() const { | ||
| 534 | return new_map_region_end; | ||
| 535 | } | ||
| 536 | |||
| 537 | u64 VMManager::GetNewMapRegionSize() const { | ||
| 538 | return new_map_region_end - new_map_region_base; | ||
| 539 | } | ||
| 540 | |||
| 541 | VAddr VMManager::GetTLSIORegionBaseAddress() const { | ||
| 542 | return tls_io_region_base; | ||
| 543 | } | ||
| 544 | |||
| 545 | VAddr VMManager::GetTLSIORegionEndAddress() const { | ||
| 546 | return tls_io_region_end; | ||
| 547 | } | ||
| 548 | |||
| 549 | u64 VMManager::GetTLSIORegionSize() const { | ||
| 550 | return tls_io_region_end - tls_io_region_base; | ||
| 403 | } | 551 | } |
| 404 | 552 | ||
| 405 | } // namespace Kernel | 553 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 98bd04bea..015559a64 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h | |||
| @@ -12,6 +12,10 @@ | |||
| 12 | #include "core/memory.h" | 12 | #include "core/memory.h" |
| 13 | #include "core/memory_hook.h" | 13 | #include "core/memory_hook.h" |
| 14 | 14 | ||
| 15 | namespace FileSys { | ||
| 16 | enum class ProgramAddressSpaceType : u8; | ||
| 17 | } | ||
| 18 | |||
| 15 | namespace Kernel { | 19 | namespace Kernel { |
| 16 | 20 | ||
| 17 | enum class VMAType : u8 { | 21 | enum class VMAType : u8 { |
| @@ -81,7 +85,7 @@ struct VirtualMemoryArea { | |||
| 81 | /// Memory block backing this VMA. | 85 | /// Memory block backing this VMA. |
| 82 | std::shared_ptr<std::vector<u8>> backing_block = nullptr; | 86 | std::shared_ptr<std::vector<u8>> backing_block = nullptr; |
| 83 | /// Offset into the backing_memory the mapping starts from. | 87 | /// Offset into the backing_memory the mapping starts from. |
| 84 | size_t offset = 0; | 88 | std::size_t offset = 0; |
| 85 | 89 | ||
| 86 | // Settings for type = BackingMemory | 90 | // Settings for type = BackingMemory |
| 87 | /// Pointer backing this VMA. It will not be destroyed or freed when the VMA is removed. | 91 | /// Pointer backing this VMA. It will not be destroyed or freed when the VMA is removed. |
| @@ -111,12 +115,6 @@ struct VirtualMemoryArea { | |||
| 111 | class VMManager final { | 115 | class VMManager final { |
| 112 | public: | 116 | public: |
| 113 | /** | 117 | /** |
| 114 | * The maximum amount of address space managed by the kernel. | ||
| 115 | * @todo This was selected arbitrarily, and should be verified for Switch OS. | ||
| 116 | */ | ||
| 117 | static constexpr VAddr MAX_ADDRESS{0x1000000000ULL}; | ||
| 118 | |||
| 119 | /** | ||
| 120 | * A map covering the entirety of the managed address space, keyed by the `base` field of each | 118 | * A map covering the entirety of the managed address space, keyed by the `base` field of each |
| 121 | * VMA. It must always be modified by splitting or merging VMAs, so that the invariant | 119 | * VMA. It must always be modified by splitting or merging VMAs, so that the invariant |
| 122 | * `elem.base + elem.size == next.base` is preserved, and mergeable regions must always be | 120 | * `elem.base + elem.size == next.base` is preserved, and mergeable regions must always be |
| @@ -130,7 +128,7 @@ public: | |||
| 130 | ~VMManager(); | 128 | ~VMManager(); |
| 131 | 129 | ||
| 132 | /// Clears the address space map, re-initializing with a single free area. | 130 | /// Clears the address space map, re-initializing with a single free area. |
| 133 | void Reset(); | 131 | void Reset(FileSys::ProgramAddressSpaceType type); |
| 134 | 132 | ||
| 135 | /// Finds the VMA in which the given address is included in, or `vma_map.end()`. | 133 | /// Finds the VMA in which the given address is included in, or `vma_map.end()`. |
| 136 | VMAHandle FindVMA(VAddr target) const; | 134 | VMAHandle FindVMA(VAddr target) const; |
| @@ -147,7 +145,7 @@ public: | |||
| 147 | * @param state MemoryState tag to attach to the VMA. | 145 | * @param state MemoryState tag to attach to the VMA. |
| 148 | */ | 146 | */ |
| 149 | ResultVal<VMAHandle> MapMemoryBlock(VAddr target, std::shared_ptr<std::vector<u8>> block, | 147 | ResultVal<VMAHandle> MapMemoryBlock(VAddr target, std::shared_ptr<std::vector<u8>> block, |
| 150 | size_t offset, u64 size, MemoryState state); | 148 | std::size_t offset, u64 size, MemoryState state); |
| 151 | 149 | ||
| 152 | /** | 150 | /** |
| 153 | * Maps an unmanaged host memory pointer at a given address. | 151 | * Maps an unmanaged host memory pointer at a given address. |
| @@ -195,12 +193,63 @@ public: | |||
| 195 | /// Gets the total heap usage, used by svcGetInfo | 193 | /// Gets the total heap usage, used by svcGetInfo |
| 196 | u64 GetTotalHeapUsage() const; | 194 | u64 GetTotalHeapUsage() const; |
| 197 | 195 | ||
| 198 | /// Gets the total address space base address, used by svcGetInfo | 196 | /// Gets the address space base address |
| 199 | VAddr GetAddressSpaceBaseAddr() const; | 197 | VAddr GetAddressSpaceBaseAddress() const; |
| 200 | 198 | ||
| 201 | /// Gets the total address space address size, used by svcGetInfo | 199 | /// Gets the address space end address |
| 200 | VAddr GetAddressSpaceEndAddress() const; | ||
| 201 | |||
| 202 | /// Gets the total address space address size in bytes | ||
| 202 | u64 GetAddressSpaceSize() const; | 203 | u64 GetAddressSpaceSize() const; |
| 203 | 204 | ||
| 205 | /// Gets the address space width in bits. | ||
| 206 | u64 GetAddressSpaceWidth() const; | ||
| 207 | |||
| 208 | /// Gets the base address of the code region. | ||
| 209 | VAddr GetCodeRegionBaseAddress() const; | ||
| 210 | |||
| 211 | /// Gets the end address of the code region. | ||
| 212 | VAddr GetCodeRegionEndAddress() const; | ||
| 213 | |||
| 214 | /// Gets the total size of the code region in bytes. | ||
| 215 | u64 GetCodeRegionSize() const; | ||
| 216 | |||
| 217 | /// Gets the base address of the heap region. | ||
| 218 | VAddr GetHeapRegionBaseAddress() const; | ||
| 219 | |||
| 220 | /// Gets the end address of the heap region; | ||
| 221 | VAddr GetHeapRegionEndAddress() const; | ||
| 222 | |||
| 223 | /// Gets the total size of the heap region in bytes. | ||
| 224 | u64 GetHeapRegionSize() const; | ||
| 225 | |||
| 226 | /// Gets the base address of the map region. | ||
| 227 | VAddr GetMapRegionBaseAddress() const; | ||
| 228 | |||
| 229 | /// Gets the end address of the map region. | ||
| 230 | VAddr GetMapRegionEndAddress() const; | ||
| 231 | |||
| 232 | /// Gets the total size of the map region in bytes. | ||
| 233 | u64 GetMapRegionSize() const; | ||
| 234 | |||
| 235 | /// Gets the base address of the new map region. | ||
| 236 | VAddr GetNewMapRegionBaseAddress() const; | ||
| 237 | |||
| 238 | /// Gets the end address of the new map region. | ||
| 239 | VAddr GetNewMapRegionEndAddress() const; | ||
| 240 | |||
| 241 | /// Gets the total size of the new map region in bytes. | ||
| 242 | u64 GetNewMapRegionSize() const; | ||
| 243 | |||
| 244 | /// Gets the base address of the TLS IO region. | ||
| 245 | VAddr GetTLSIORegionBaseAddress() const; | ||
| 246 | |||
| 247 | /// Gets the end address of the TLS IO region. | ||
| 248 | VAddr GetTLSIORegionEndAddress() const; | ||
| 249 | |||
| 250 | /// Gets the total size of the TLS IO region in bytes. | ||
| 251 | u64 GetTLSIORegionSize() const; | ||
| 252 | |||
| 204 | /// Each VMManager has its own page table, which is set as the main one when the owning process | 253 | /// Each VMManager has its own page table, which is set as the main one when the owning process |
| 205 | /// is scheduled. | 254 | /// is scheduled. |
| 206 | Memory::PageTable page_table; | 255 | Memory::PageTable page_table; |
| @@ -240,5 +289,36 @@ private: | |||
| 240 | 289 | ||
| 241 | /// Updates the pages corresponding to this VMA so they match the VMA's attributes. | 290 | /// Updates the pages corresponding to this VMA so they match the VMA's attributes. |
| 242 | void UpdatePageTableForVMA(const VirtualMemoryArea& vma); | 291 | void UpdatePageTableForVMA(const VirtualMemoryArea& vma); |
| 292 | |||
| 293 | /// Initializes memory region ranges to adhere to a given address space type. | ||
| 294 | void InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType type); | ||
| 295 | |||
| 296 | /// Clears the underlying map and page table. | ||
| 297 | void Clear(); | ||
| 298 | |||
| 299 | /// Clears out the VMA map, unmapping any previously mapped ranges. | ||
| 300 | void ClearVMAMap(); | ||
| 301 | |||
| 302 | /// Clears out the page table | ||
| 303 | void ClearPageTable(); | ||
| 304 | |||
| 305 | u32 address_space_width = 0; | ||
| 306 | VAddr address_space_base = 0; | ||
| 307 | VAddr address_space_end = 0; | ||
| 308 | |||
| 309 | VAddr code_region_base = 0; | ||
| 310 | VAddr code_region_end = 0; | ||
| 311 | |||
| 312 | VAddr heap_region_base = 0; | ||
| 313 | VAddr heap_region_end = 0; | ||
| 314 | |||
| 315 | VAddr map_region_base = 0; | ||
| 316 | VAddr map_region_end = 0; | ||
| 317 | |||
| 318 | VAddr new_map_region_base = 0; | ||
| 319 | VAddr new_map_region_end = 0; | ||
| 320 | |||
| 321 | VAddr tls_io_region_base = 0; | ||
| 322 | VAddr tls_io_region_end = 0; | ||
| 243 | }; | 323 | }; |
| 244 | } // namespace Kernel | 324 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/wait_object.cpp b/src/core/hle/kernel/wait_object.cpp index eef00b729..b190ceb98 100644 --- a/src/core/hle/kernel/wait_object.cpp +++ b/src/core/hle/kernel/wait_object.cpp | |||
| @@ -81,7 +81,7 @@ void WaitObject::WakeupWaitingThread(SharedPtr<Thread> thread) { | |||
| 81 | } | 81 | } |
| 82 | } | 82 | } |
| 83 | 83 | ||
| 84 | size_t index = thread->GetWaitObjectIndex(this); | 84 | std::size_t index = thread->GetWaitObjectIndex(this); |
| 85 | 85 | ||
| 86 | for (auto& object : thread->wait_objects) | 86 | for (auto& object : thread->wait_objects) |
| 87 | object->RemoveWaitingThread(thread.get()); | 87 | object->RemoveWaitingThread(thread.get()); |
diff --git a/src/core/hle/kernel/wait_object.h b/src/core/hle/kernel/wait_object.h index 0bd97133c..f4367ee28 100644 --- a/src/core/hle/kernel/wait_object.h +++ b/src/core/hle/kernel/wait_object.h | |||
| @@ -69,7 +69,7 @@ private: | |||
| 69 | template <> | 69 | template <> |
| 70 | inline SharedPtr<WaitObject> DynamicObjectCast<WaitObject>(SharedPtr<Object> object) { | 70 | inline SharedPtr<WaitObject> DynamicObjectCast<WaitObject>(SharedPtr<Object> object) { |
| 71 | if (object != nullptr && object->IsWaitable()) { | 71 | if (object != nullptr && object->IsWaitable()) { |
| 72 | return boost::static_pointer_cast<WaitObject>(std::move(object)); | 72 | return boost::static_pointer_cast<WaitObject>(object); |
| 73 | } | 73 | } |
| 74 | return nullptr; | 74 | return nullptr; |
| 75 | } | 75 | } |
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index 1502dbf55..e61748ca3 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp | |||
| @@ -34,7 +34,7 @@ public: | |||
| 34 | static const FunctionInfo functions[] = { | 34 | static const FunctionInfo functions[] = { |
| 35 | {0, &IProfile::Get, "Get"}, | 35 | {0, &IProfile::Get, "Get"}, |
| 36 | {1, &IProfile::GetBase, "GetBase"}, | 36 | {1, &IProfile::GetBase, "GetBase"}, |
| 37 | {10, nullptr, "GetImageSize"}, | 37 | {10, &IProfile::GetImageSize, "GetImageSize"}, |
| 38 | {11, &IProfile::LoadImage, "LoadImage"}, | 38 | {11, &IProfile::LoadImage, "LoadImage"}, |
| 39 | }; | 39 | }; |
| 40 | RegisterHandlers(functions); | 40 | RegisterHandlers(functions); |
| @@ -93,6 +93,14 @@ private: | |||
| 93 | rb.Push<u32>(jpeg_size); | 93 | rb.Push<u32>(jpeg_size); |
| 94 | } | 94 | } |
| 95 | 95 | ||
| 96 | void GetImageSize(Kernel::HLERequestContext& ctx) { | ||
| 97 | LOG_WARNING(Service_ACC, "(STUBBED) called"); | ||
| 98 | constexpr u32 jpeg_size = 107; | ||
| 99 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 100 | rb.Push(RESULT_SUCCESS); | ||
| 101 | rb.Push<u32>(jpeg_size); | ||
| 102 | } | ||
| 103 | |||
| 96 | const ProfileManager& profile_manager; | 104 | const ProfileManager& profile_manager; |
| 97 | UUID user_id; ///< The user id this profile refers to. | 105 | UUID user_id; ///< The user id this profile refers to. |
| 98 | }; | 106 | }; |
| @@ -122,11 +130,10 @@ private: | |||
| 122 | 130 | ||
| 123 | void GetAccountId(Kernel::HLERequestContext& ctx) { | 131 | void GetAccountId(Kernel::HLERequestContext& ctx) { |
| 124 | LOG_WARNING(Service_ACC, "(STUBBED) called"); | 132 | LOG_WARNING(Service_ACC, "(STUBBED) called"); |
| 125 | // TODO(Subv): Find out what this actually does and implement it. Stub it as an error for | 133 | // Should return a nintendo account ID |
| 126 | // now since we do not implement NNID. Returning a bogus id here will cause games to send | 134 | IPC::ResponseBuilder rb{ctx, 4}; |
| 127 | // invalid IPC requests after ListOpenUsers is called. | 135 | rb.Push(RESULT_SUCCESS); |
| 128 | IPC::ResponseBuilder rb{ctx, 2}; | 136 | rb.PushRaw<u64>(1); |
| 129 | rb.Push(ResultCode(-1)); | ||
| 130 | } | 137 | } |
| 131 | }; | 138 | }; |
| 132 | 139 | ||
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp index 4ccebef23..bcb3475db 100644 --- a/src/core/hle/service/acc/profile_manager.cpp +++ b/src/core/hle/service/acc/profile_manager.cpp | |||
| @@ -25,7 +25,7 @@ const UUID& UUID::Generate() { | |||
| 25 | ProfileManager::ProfileManager() { | 25 | ProfileManager::ProfileManager() { |
| 26 | // TODO(ogniK): Create the default user we have for now until loading/saving users is added | 26 | // TODO(ogniK): Create the default user we have for now until loading/saving users is added |
| 27 | auto user_uuid = UUID{1, 0}; | 27 | auto user_uuid = UUID{1, 0}; |
| 28 | CreateNewUser(user_uuid, Settings::values.username); | 28 | ASSERT(CreateNewUser(user_uuid, Settings::values.username).IsSuccess()); |
| 29 | OpenUser(user_uuid); | 29 | OpenUser(user_uuid); |
| 30 | } | 30 | } |
| 31 | 31 | ||
| @@ -33,7 +33,7 @@ ProfileManager::~ProfileManager() = default; | |||
| 33 | 33 | ||
| 34 | /// After a users creation it needs to be "registered" to the system. AddToProfiles handles the | 34 | /// After a users creation it needs to be "registered" to the system. AddToProfiles handles the |
| 35 | /// internal management of the users profiles | 35 | /// internal management of the users profiles |
| 36 | boost::optional<size_t> ProfileManager::AddToProfiles(const ProfileInfo& user) { | 36 | boost::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& user) { |
| 37 | if (user_count >= MAX_USERS) { | 37 | if (user_count >= MAX_USERS) { |
| 38 | return boost::none; | 38 | return boost::none; |
| 39 | } | 39 | } |
| @@ -42,7 +42,7 @@ boost::optional<size_t> ProfileManager::AddToProfiles(const ProfileInfo& user) { | |||
| 42 | } | 42 | } |
| 43 | 43 | ||
| 44 | /// Deletes a specific profile based on it's profile index | 44 | /// Deletes a specific profile based on it's profile index |
| 45 | bool ProfileManager::RemoveProfileAtIndex(size_t index) { | 45 | bool ProfileManager::RemoveProfileAtIndex(std::size_t index) { |
| 46 | if (index >= MAX_USERS || index >= user_count) { | 46 | if (index >= MAX_USERS || index >= user_count) { |
| 47 | return false; | 47 | return false; |
| 48 | } | 48 | } |
| @@ -91,7 +91,8 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const ProfileUsername& usern | |||
| 91 | /// specifically by allowing an std::string for the username. This is required specifically since | 91 | /// specifically by allowing an std::string for the username. This is required specifically since |
| 92 | /// we're loading a string straight from the config | 92 | /// we're loading a string straight from the config |
| 93 | ResultCode ProfileManager::CreateNewUser(UUID uuid, const std::string& username) { | 93 | ResultCode ProfileManager::CreateNewUser(UUID uuid, const std::string& username) { |
| 94 | ProfileUsername username_output; | 94 | ProfileUsername username_output{}; |
| 95 | |||
| 95 | if (username.size() > username_output.size()) { | 96 | if (username.size() > username_output.size()) { |
| 96 | std::copy_n(username.begin(), username_output.size(), username_output.begin()); | 97 | std::copy_n(username.begin(), username_output.size(), username_output.begin()); |
| 97 | } else { | 98 | } else { |
| @@ -101,7 +102,7 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const std::string& username) | |||
| 101 | } | 102 | } |
| 102 | 103 | ||
| 103 | /// Returns a users profile index based on their user id. | 104 | /// Returns a users profile index based on their user id. |
| 104 | boost::optional<size_t> ProfileManager::GetUserIndex(const UUID& uuid) const { | 105 | boost::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const { |
| 105 | if (!uuid) { | 106 | if (!uuid) { |
| 106 | return boost::none; | 107 | return boost::none; |
| 107 | } | 108 | } |
| @@ -110,16 +111,17 @@ boost::optional<size_t> ProfileManager::GetUserIndex(const UUID& uuid) const { | |||
| 110 | if (iter == profiles.end()) { | 111 | if (iter == profiles.end()) { |
| 111 | return boost::none; | 112 | return boost::none; |
| 112 | } | 113 | } |
| 113 | return static_cast<size_t>(std::distance(profiles.begin(), iter)); | 114 | return static_cast<std::size_t>(std::distance(profiles.begin(), iter)); |
| 114 | } | 115 | } |
| 115 | 116 | ||
| 116 | /// Returns a users profile index based on their profile | 117 | /// Returns a users profile index based on their profile |
| 117 | boost::optional<size_t> ProfileManager::GetUserIndex(const ProfileInfo& user) const { | 118 | boost::optional<std::size_t> ProfileManager::GetUserIndex(const ProfileInfo& user) const { |
| 118 | return GetUserIndex(user.user_uuid); | 119 | return GetUserIndex(user.user_uuid); |
| 119 | } | 120 | } |
| 120 | 121 | ||
| 121 | /// Returns the data structure used by the switch when GetProfileBase is called on acc:* | 122 | /// Returns the data structure used by the switch when GetProfileBase is called on acc:* |
| 122 | bool ProfileManager::GetProfileBase(boost::optional<size_t> index, ProfileBase& profile) const { | 123 | bool ProfileManager::GetProfileBase(boost::optional<std::size_t> index, |
| 124 | ProfileBase& profile) const { | ||
| 123 | if (index == boost::none || index >= MAX_USERS) { | 125 | if (index == boost::none || index >= MAX_USERS) { |
| 124 | return false; | 126 | return false; |
| 125 | } | 127 | } |
| @@ -143,14 +145,16 @@ bool ProfileManager::GetProfileBase(const ProfileInfo& user, ProfileBase& profil | |||
| 143 | 145 | ||
| 144 | /// Returns the current user count on the system. We keep a variable which tracks the count so we | 146 | /// Returns the current user count on the system. We keep a variable which tracks the count so we |
| 145 | /// don't have to loop the internal profile array every call. | 147 | /// don't have to loop the internal profile array every call. |
| 146 | size_t ProfileManager::GetUserCount() const { | 148 | |
| 149 | std::size_t ProfileManager::GetUserCount() const { | ||
| 147 | return user_count; | 150 | return user_count; |
| 148 | } | 151 | } |
| 149 | 152 | ||
| 150 | /// Lists the current "opened" users on the system. Users are typically not open until they sign | 153 | /// Lists the current "opened" users on the system. Users are typically not open until they sign |
| 151 | /// into something or pick a profile. As of right now users should all be open until qlaunch is | 154 | /// into something or pick a profile. As of right now users should all be open until qlaunch is |
| 152 | /// booting | 155 | /// booting |
| 153 | size_t ProfileManager::GetOpenUserCount() const { | 156 | |
| 157 | std::size_t ProfileManager::GetOpenUserCount() const { | ||
| 154 | return std::count_if(profiles.begin(), profiles.end(), | 158 | return std::count_if(profiles.begin(), profiles.end(), |
| 155 | [](const ProfileInfo& p) { return p.is_open; }); | 159 | [](const ProfileInfo& p) { return p.is_open; }); |
| 156 | } | 160 | } |
| @@ -206,7 +210,7 @@ UUID ProfileManager::GetLastOpenedUser() const { | |||
| 206 | } | 210 | } |
| 207 | 211 | ||
| 208 | /// Return the users profile base and the unknown arbitary data. | 212 | /// Return the users profile base and the unknown arbitary data. |
| 209 | bool ProfileManager::GetProfileBaseAndData(boost::optional<size_t> index, ProfileBase& profile, | 213 | bool ProfileManager::GetProfileBaseAndData(boost::optional<std::size_t> index, ProfileBase& profile, |
| 210 | ProfileData& data) const { | 214 | ProfileData& data) const { |
| 211 | if (GetProfileBase(index, profile)) { | 215 | if (GetProfileBase(index, profile)) { |
| 212 | data = profiles[index.get()].data; | 216 | data = profiles[index.get()].data; |
diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h index cd8df93a5..bffd4cf4d 100644 --- a/src/core/hle/service/acc/profile_manager.h +++ b/src/core/hle/service/acc/profile_manager.h | |||
| @@ -12,8 +12,8 @@ | |||
| 12 | #include "core/hle/result.h" | 12 | #include "core/hle/result.h" |
| 13 | 13 | ||
| 14 | namespace Service::Account { | 14 | namespace Service::Account { |
| 15 | constexpr size_t MAX_USERS = 8; | 15 | constexpr std::size_t MAX_USERS = 8; |
| 16 | constexpr size_t MAX_DATA = 128; | 16 | constexpr std::size_t MAX_DATA = 128; |
| 17 | constexpr u128 INVALID_UUID{{0, 0}}; | 17 | constexpr u128 INVALID_UUID{{0, 0}}; |
| 18 | 18 | ||
| 19 | struct UUID { | 19 | struct UUID { |
| @@ -87,18 +87,18 @@ public: | |||
| 87 | ResultCode AddUser(const ProfileInfo& user); | 87 | ResultCode AddUser(const ProfileInfo& user); |
| 88 | ResultCode CreateNewUser(UUID uuid, const ProfileUsername& username); | 88 | ResultCode CreateNewUser(UUID uuid, const ProfileUsername& username); |
| 89 | ResultCode CreateNewUser(UUID uuid, const std::string& username); | 89 | ResultCode CreateNewUser(UUID uuid, const std::string& username); |
| 90 | boost::optional<size_t> GetUserIndex(const UUID& uuid) const; | 90 | boost::optional<std::size_t> GetUserIndex(const UUID& uuid) const; |
| 91 | boost::optional<size_t> GetUserIndex(const ProfileInfo& user) const; | 91 | boost::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const; |
| 92 | bool GetProfileBase(boost::optional<size_t> index, ProfileBase& profile) const; | 92 | bool GetProfileBase(boost::optional<std::size_t> index, ProfileBase& profile) const; |
| 93 | bool GetProfileBase(UUID uuid, ProfileBase& profile) const; | 93 | bool GetProfileBase(UUID uuid, ProfileBase& profile) const; |
| 94 | bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const; | 94 | bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const; |
| 95 | bool GetProfileBaseAndData(boost::optional<size_t> index, ProfileBase& profile, | 95 | bool GetProfileBaseAndData(boost::optional<std::size_t> index, ProfileBase& profile, |
| 96 | ProfileData& data) const; | 96 | ProfileData& data) const; |
| 97 | bool GetProfileBaseAndData(UUID uuid, ProfileBase& profile, ProfileData& data) const; | 97 | bool GetProfileBaseAndData(UUID uuid, ProfileBase& profile, ProfileData& data) const; |
| 98 | bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile, | 98 | bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile, |
| 99 | ProfileData& data) const; | 99 | ProfileData& data) const; |
| 100 | size_t GetUserCount() const; | 100 | std::size_t GetUserCount() const; |
| 101 | size_t GetOpenUserCount() const; | 101 | std::size_t GetOpenUserCount() const; |
| 102 | bool UserExists(UUID uuid) const; | 102 | bool UserExists(UUID uuid) const; |
| 103 | void OpenUser(UUID uuid); | 103 | void OpenUser(UUID uuid); |
| 104 | void CloseUser(UUID uuid); | 104 | void CloseUser(UUID uuid); |
| @@ -110,9 +110,9 @@ public: | |||
| 110 | 110 | ||
| 111 | private: | 111 | private: |
| 112 | std::array<ProfileInfo, MAX_USERS> profiles{}; | 112 | std::array<ProfileInfo, MAX_USERS> profiles{}; |
| 113 | size_t user_count = 0; | 113 | std::size_t user_count = 0; |
| 114 | boost::optional<size_t> AddToProfiles(const ProfileInfo& profile); | 114 | boost::optional<std::size_t> AddToProfiles(const ProfileInfo& profile); |
| 115 | bool RemoveProfileAtIndex(size_t index); | 115 | bool RemoveProfileAtIndex(std::size_t index); |
| 116 | UUID last_opened_user{INVALID_UUID}; | 116 | UUID last_opened_user{INVALID_UUID}; |
| 117 | }; | 117 | }; |
| 118 | 118 | ||
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index a57ed3042..69bfce1c1 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp | |||
| @@ -20,6 +20,7 @@ | |||
| 20 | #include "core/hle/service/nvflinger/nvflinger.h" | 20 | #include "core/hle/service/nvflinger/nvflinger.h" |
| 21 | #include "core/hle/service/pm/pm.h" | 21 | #include "core/hle/service/pm/pm.h" |
| 22 | #include "core/hle/service/set/set.h" | 22 | #include "core/hle/service/set/set.h" |
| 23 | #include "core/hle/service/vi/vi.h" | ||
| 23 | #include "core/settings.h" | 24 | #include "core/settings.h" |
| 24 | 25 | ||
| 25 | namespace Service::AM { | 26 | namespace Service::AM { |
| @@ -334,7 +335,7 @@ ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter" | |||
| 334 | {51, nullptr, "SetVrModeEnabled"}, | 335 | {51, nullptr, "SetVrModeEnabled"}, |
| 335 | {52, nullptr, "SwitchLcdBacklight"}, | 336 | {52, nullptr, "SwitchLcdBacklight"}, |
| 336 | {55, nullptr, "IsInControllerFirmwareUpdateSection"}, | 337 | {55, nullptr, "IsInControllerFirmwareUpdateSection"}, |
| 337 | {60, nullptr, "GetDefaultDisplayResolution"}, | 338 | {60, &ICommonStateGetter::GetDefaultDisplayResolution, "GetDefaultDisplayResolution"}, |
| 338 | {61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, | 339 | {61, &ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent, |
| 339 | "GetDefaultDisplayResolutionChangeEvent"}, | 340 | "GetDefaultDisplayResolutionChangeEvent"}, |
| 340 | {62, nullptr, "GetHdcpAuthenticationState"}, | 341 | {62, nullptr, "GetHdcpAuthenticationState"}, |
| @@ -393,6 +394,21 @@ void ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent(Kernel::HLEReque | |||
| 393 | LOG_WARNING(Service_AM, "(STUBBED) called"); | 394 | LOG_WARNING(Service_AM, "(STUBBED) called"); |
| 394 | } | 395 | } |
| 395 | 396 | ||
| 397 | void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx) { | ||
| 398 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 399 | rb.Push(RESULT_SUCCESS); | ||
| 400 | |||
| 401 | if (Settings::values.use_docked_mode) { | ||
| 402 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth)); | ||
| 403 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight)); | ||
| 404 | } else { | ||
| 405 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth)); | ||
| 406 | rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight)); | ||
| 407 | } | ||
| 408 | |||
| 409 | LOG_DEBUG(Service_AM, "called"); | ||
| 410 | } | ||
| 411 | |||
| 396 | void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) { | 412 | void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) { |
| 397 | const bool use_docked_mode{Settings::values.use_docked_mode}; | 413 | const bool use_docked_mode{Settings::values.use_docked_mode}; |
| 398 | IPC::ResponseBuilder rb{ctx, 3}; | 414 | IPC::ResponseBuilder rb{ctx, 3}; |
| @@ -446,7 +462,7 @@ private: | |||
| 446 | 462 | ||
| 447 | std::memcpy(&buffer[offset], data.data(), data.size()); | 463 | std::memcpy(&buffer[offset], data.data(), data.size()); |
| 448 | 464 | ||
| 449 | IPC::ResponseBuilder rb{rp.MakeBuilder(2, 0, 0)}; | 465 | IPC::ResponseBuilder rb{ctx, 2}; |
| 450 | rb.Push(RESULT_SUCCESS); | 466 | rb.Push(RESULT_SUCCESS); |
| 451 | 467 | ||
| 452 | LOG_DEBUG(Service_AM, "called, offset={}", offset); | 468 | LOG_DEBUG(Service_AM, "called, offset={}", offset); |
| @@ -456,13 +472,13 @@ private: | |||
| 456 | IPC::RequestParser rp{ctx}; | 472 | IPC::RequestParser rp{ctx}; |
| 457 | 473 | ||
| 458 | const u64 offset{rp.Pop<u64>()}; | 474 | const u64 offset{rp.Pop<u64>()}; |
| 459 | const size_t size{ctx.GetWriteBufferSize()}; | 475 | const std::size_t size{ctx.GetWriteBufferSize()}; |
| 460 | 476 | ||
| 461 | ASSERT(offset + size <= buffer.size()); | 477 | ASSERT(offset + size <= buffer.size()); |
| 462 | 478 | ||
| 463 | ctx.WriteBuffer(buffer.data() + offset, size); | 479 | ctx.WriteBuffer(buffer.data() + offset, size); |
| 464 | 480 | ||
| 465 | IPC::ResponseBuilder rb{rp.MakeBuilder(2, 0, 0)}; | 481 | IPC::ResponseBuilder rb{ctx, 2}; |
| 466 | rb.Push(RESULT_SUCCESS); | 482 | rb.Push(RESULT_SUCCESS); |
| 467 | 483 | ||
| 468 | LOG_DEBUG(Service_AM, "called, offset={}", offset); | 484 | LOG_DEBUG(Service_AM, "called, offset={}", offset); |
| @@ -552,7 +568,7 @@ private: | |||
| 552 | IPC::RequestParser rp{ctx}; | 568 | IPC::RequestParser rp{ctx}; |
| 553 | storage_stack.push(rp.PopIpcInterface<AM::IStorage>()); | 569 | storage_stack.push(rp.PopIpcInterface<AM::IStorage>()); |
| 554 | 570 | ||
| 555 | IPC::ResponseBuilder rb{rp.MakeBuilder(2, 0, 0)}; | 571 | IPC::ResponseBuilder rb{ctx, 2}; |
| 556 | rb.Push(RESULT_SUCCESS); | 572 | rb.Push(RESULT_SUCCESS); |
| 557 | 573 | ||
| 558 | LOG_DEBUG(Service_AM, "called"); | 574 | LOG_DEBUG(Service_AM, "called"); |
| @@ -600,7 +616,7 @@ void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) { | |||
| 600 | const u64 size{rp.Pop<u64>()}; | 616 | const u64 size{rp.Pop<u64>()}; |
| 601 | std::vector<u8> buffer(size); | 617 | std::vector<u8> buffer(size); |
| 602 | 618 | ||
| 603 | IPC::ResponseBuilder rb{rp.MakeBuilder(2, 0, 1)}; | 619 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 604 | rb.Push(RESULT_SUCCESS); | 620 | rb.Push(RESULT_SUCCESS); |
| 605 | rb.PushIpcInterface<AM::IStorage>(std::move(buffer)); | 621 | rb.PushIpcInterface<AM::IStorage>(std::move(buffer)); |
| 606 | 622 | ||
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h index fd9ae296b..b39b0d838 100644 --- a/src/core/hle/service/am/am.h +++ b/src/core/hle/service/am/am.h | |||
| @@ -123,6 +123,7 @@ private: | |||
| 123 | void GetOperationMode(Kernel::HLERequestContext& ctx); | 123 | void GetOperationMode(Kernel::HLERequestContext& ctx); |
| 124 | void GetPerformanceMode(Kernel::HLERequestContext& ctx); | 124 | void GetPerformanceMode(Kernel::HLERequestContext& ctx); |
| 125 | void GetBootMode(Kernel::HLERequestContext& ctx); | 125 | void GetBootMode(Kernel::HLERequestContext& ctx); |
| 126 | void GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx); | ||
| 126 | 127 | ||
| 127 | Kernel::SharedPtr<Kernel::Event> event; | 128 | Kernel::SharedPtr<Kernel::Event> event; |
| 128 | }; | 129 | }; |
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index 80a002322..ff1edefbb 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp | |||
| @@ -190,7 +190,7 @@ void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) { | |||
| 190 | 190 | ||
| 191 | ctx.WriteBuffer(DefaultDevice); | 191 | ctx.WriteBuffer(DefaultDevice); |
| 192 | 192 | ||
| 193 | IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0); | 193 | IPC::ResponseBuilder rb{ctx, 3}; |
| 194 | 194 | ||
| 195 | rb.Push(RESULT_SUCCESS); | 195 | rb.Push(RESULT_SUCCESS); |
| 196 | rb.Push<u32>(1); // Amount of audio devices | 196 | rb.Push<u32>(1); // Amount of audio devices |
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp index e84c4fa2b..6073f4ecd 100644 --- a/src/core/hle/service/audio/audren_u.cpp +++ b/src/core/hle/service/audio/audren_u.cpp | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | #include "common/alignment.h" | 10 | #include "common/alignment.h" |
| 11 | #include "common/common_funcs.h" | 11 | #include "common/common_funcs.h" |
| 12 | #include "common/logging/log.h" | 12 | #include "common/logging/log.h" |
| 13 | #include "core/core.h" | ||
| 13 | #include "core/hle/ipc_helpers.h" | 14 | #include "core/hle/ipc_helpers.h" |
| 14 | #include "core/hle/kernel/event.h" | 15 | #include "core/hle/kernel/event.h" |
| 15 | #include "core/hle/kernel/hle_ipc.h" | 16 | #include "core/hle/kernel/hle_ipc.h" |
| @@ -25,7 +26,7 @@ public: | |||
| 25 | {0, &IAudioRenderer::GetAudioRendererSampleRate, "GetAudioRendererSampleRate"}, | 26 | {0, &IAudioRenderer::GetAudioRendererSampleRate, "GetAudioRendererSampleRate"}, |
| 26 | {1, &IAudioRenderer::GetAudioRendererSampleCount, "GetAudioRendererSampleCount"}, | 27 | {1, &IAudioRenderer::GetAudioRendererSampleCount, "GetAudioRendererSampleCount"}, |
| 27 | {2, &IAudioRenderer::GetAudioRendererMixBufferCount, "GetAudioRendererMixBufferCount"}, | 28 | {2, &IAudioRenderer::GetAudioRendererMixBufferCount, "GetAudioRendererMixBufferCount"}, |
| 28 | {3, nullptr, "GetAudioRendererState"}, | 29 | {3, &IAudioRenderer::GetAudioRendererState, "GetAudioRendererState"}, |
| 29 | {4, &IAudioRenderer::RequestUpdateAudioRenderer, "RequestUpdateAudioRenderer"}, | 30 | {4, &IAudioRenderer::RequestUpdateAudioRenderer, "RequestUpdateAudioRenderer"}, |
| 30 | {5, &IAudioRenderer::StartAudioRenderer, "StartAudioRenderer"}, | 31 | {5, &IAudioRenderer::StartAudioRenderer, "StartAudioRenderer"}, |
| 31 | {6, &IAudioRenderer::StopAudioRenderer, "StopAudioRenderer"}, | 32 | {6, &IAudioRenderer::StopAudioRenderer, "StopAudioRenderer"}, |
| @@ -62,6 +63,13 @@ private: | |||
| 62 | LOG_DEBUG(Service_Audio, "called"); | 63 | LOG_DEBUG(Service_Audio, "called"); |
| 63 | } | 64 | } |
| 64 | 65 | ||
| 66 | void GetAudioRendererState(Kernel::HLERequestContext& ctx) { | ||
| 67 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 68 | rb.Push(RESULT_SUCCESS); | ||
| 69 | rb.Push<u32>(static_cast<u32>(renderer->GetStreamState())); | ||
| 70 | LOG_DEBUG(Service_Audio, "called"); | ||
| 71 | } | ||
| 72 | |||
| 65 | void GetAudioRendererMixBufferCount(Kernel::HLERequestContext& ctx) { | 73 | void GetAudioRendererMixBufferCount(Kernel::HLERequestContext& ctx) { |
| 66 | IPC::ResponseBuilder rb{ctx, 3}; | 74 | IPC::ResponseBuilder rb{ctx, 3}; |
| 67 | rb.Push(RESULT_SUCCESS); | 75 | rb.Push(RESULT_SUCCESS); |
| @@ -137,7 +145,7 @@ private: | |||
| 137 | constexpr std::array<char, 15> audio_interface{{"AudioInterface"}}; | 145 | constexpr std::array<char, 15> audio_interface{{"AudioInterface"}}; |
| 138 | ctx.WriteBuffer(audio_interface); | 146 | ctx.WriteBuffer(audio_interface); |
| 139 | 147 | ||
| 140 | IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0); | 148 | IPC::ResponseBuilder rb{ctx, 3}; |
| 141 | rb.Push(RESULT_SUCCESS); | 149 | rb.Push(RESULT_SUCCESS); |
| 142 | rb.Push<u32>(1); | 150 | rb.Push<u32>(1); |
| 143 | } | 151 | } |
| @@ -151,7 +159,7 @@ private: | |||
| 151 | auto file_buffer = ctx.ReadBuffer(); | 159 | auto file_buffer = ctx.ReadBuffer(); |
| 152 | auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0'); | 160 | auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0'); |
| 153 | 161 | ||
| 154 | IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); | 162 | IPC::ResponseBuilder rb{ctx, 2}; |
| 155 | rb.Push(RESULT_SUCCESS); | 163 | rb.Push(RESULT_SUCCESS); |
| 156 | } | 164 | } |
| 157 | 165 | ||
| @@ -162,7 +170,7 @@ private: | |||
| 162 | constexpr std::array<char, 12> audio_interface{{"AudioDevice"}}; | 170 | constexpr std::array<char, 12> audio_interface{{"AudioDevice"}}; |
| 163 | ctx.WriteBuffer(audio_interface); | 171 | ctx.WriteBuffer(audio_interface); |
| 164 | 172 | ||
| 165 | IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0); | 173 | IPC::ResponseBuilder rb{ctx, 3}; |
| 166 | rb.Push(RESULT_SUCCESS); | 174 | rb.Push(RESULT_SUCCESS); |
| 167 | rb.Push<u32>(1); | 175 | rb.Push<u32>(1); |
| 168 | } | 176 | } |
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp index 668fef145..fc6067e59 100644 --- a/src/core/hle/service/audio/hwopus.cpp +++ b/src/core/hle/service/audio/hwopus.cpp | |||
| @@ -61,7 +61,7 @@ private: | |||
| 61 | 61 | ||
| 62 | bool Decoder_DecodeInterleaved(u32& consumed, u32& sample_count, const std::vector<u8>& input, | 62 | bool Decoder_DecodeInterleaved(u32& consumed, u32& sample_count, const std::vector<u8>& input, |
| 63 | std::vector<opus_int16>& output) { | 63 | std::vector<opus_int16>& output) { |
| 64 | size_t raw_output_sz = output.size() * sizeof(opus_int16); | 64 | std::size_t raw_output_sz = output.size() * sizeof(opus_int16); |
| 65 | if (sizeof(OpusHeader) > input.size()) | 65 | if (sizeof(OpusHeader) > input.size()) |
| 66 | return false; | 66 | return false; |
| 67 | OpusHeader hdr{}; | 67 | OpusHeader hdr{}; |
| @@ -96,7 +96,7 @@ private: | |||
| 96 | u32 channel_count; | 96 | u32 channel_count; |
| 97 | }; | 97 | }; |
| 98 | 98 | ||
| 99 | static size_t WorkerBufferSize(u32 channel_count) { | 99 | static std::size_t WorkerBufferSize(u32 channel_count) { |
| 100 | ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); | 100 | ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); |
| 101 | return opus_decoder_get_size(static_cast<int>(channel_count)); | 101 | return opus_decoder_get_size(static_cast<int>(channel_count)); |
| 102 | } | 102 | } |
| @@ -129,7 +129,7 @@ void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) { | |||
| 129 | "Invalid sample rate"); | 129 | "Invalid sample rate"); |
| 130 | ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); | 130 | ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); |
| 131 | 131 | ||
| 132 | size_t worker_sz = WorkerBufferSize(channel_count); | 132 | std::size_t worker_sz = WorkerBufferSize(channel_count); |
| 133 | ASSERT_MSG(buffer_sz < worker_sz, "Worker buffer too large"); | 133 | ASSERT_MSG(buffer_sz < worker_sz, "Worker buffer too large"); |
| 134 | std::unique_ptr<OpusDecoder, OpusDeleter> decoder{ | 134 | std::unique_ptr<OpusDecoder, OpusDeleter> decoder{ |
| 135 | static_cast<OpusDecoder*>(operator new(worker_sz))}; | 135 | static_cast<OpusDecoder*>(operator new(worker_sz))}; |
diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp index b436ce4e6..2f15ac2a6 100644 --- a/src/core/hle/service/fatal/fatal.cpp +++ b/src/core/hle/service/fatal/fatal.cpp | |||
| @@ -2,8 +2,17 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <array> | ||
| 6 | #include <cstring> | ||
| 7 | #include <ctime> | ||
| 8 | #include <fmt/time.h> | ||
| 9 | #include "common/file_util.h" | ||
| 5 | #include "common/logging/log.h" | 10 | #include "common/logging/log.h" |
| 11 | #include "common/scm_rev.h" | ||
| 12 | #include "common/swap.h" | ||
| 13 | #include "core/core.h" | ||
| 6 | #include "core/hle/ipc_helpers.h" | 14 | #include "core/hle/ipc_helpers.h" |
| 15 | #include "core/hle/kernel/process.h" | ||
| 7 | #include "core/hle/service/fatal/fatal.h" | 16 | #include "core/hle/service/fatal/fatal.h" |
| 8 | #include "core/hle/service/fatal/fatal_p.h" | 17 | #include "core/hle/service/fatal/fatal_p.h" |
| 9 | #include "core/hle/service/fatal/fatal_u.h" | 18 | #include "core/hle/service/fatal/fatal_u.h" |
| @@ -15,16 +24,142 @@ Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) | |||
| 15 | 24 | ||
| 16 | Module::Interface::~Interface() = default; | 25 | Module::Interface::~Interface() = default; |
| 17 | 26 | ||
| 27 | struct FatalInfo { | ||
| 28 | std::array<u64_le, 31> registers{}; // TODO(ogniK): See if this actually is registers or | ||
| 29 | // not(find a game which has non zero valeus) | ||
| 30 | u64_le unk0{}; | ||
| 31 | u64_le unk1{}; | ||
| 32 | u64_le unk2{}; | ||
| 33 | u64_le unk3{}; | ||
| 34 | u64_le unk4{}; | ||
| 35 | u64_le unk5{}; | ||
| 36 | u64_le unk6{}; | ||
| 37 | |||
| 38 | std::array<u64_le, 32> backtrace{}; | ||
| 39 | u64_le unk7{}; | ||
| 40 | u64_le unk8{}; | ||
| 41 | u32_le backtrace_size{}; | ||
| 42 | u32_le unk9{}; | ||
| 43 | u32_le unk10{}; // TODO(ogniK): Is this even used or is it just padding? | ||
| 44 | }; | ||
| 45 | static_assert(sizeof(FatalInfo) == 0x250, "FatalInfo is an invalid size"); | ||
| 46 | |||
| 47 | enum class FatalType : u32 { | ||
| 48 | ErrorReportAndScreen = 0, | ||
| 49 | ErrorReport = 1, | ||
| 50 | ErrorScreen = 2, | ||
| 51 | }; | ||
| 52 | |||
| 53 | static void GenerateErrorReport(ResultCode error_code, const FatalInfo& info) { | ||
| 54 | const auto title_id = Core::CurrentProcess()->GetTitleID(); | ||
| 55 | std::string crash_report = | ||
| 56 | fmt::format("Yuzu {}-{} crash report\n" | ||
| 57 | "Title ID: {:016x}\n" | ||
| 58 | "Result: 0x{:X} ({:04}-{:04d})\n" | ||
| 59 | "\n", | ||
| 60 | Common::g_scm_branch, Common::g_scm_desc, title_id, error_code.raw, | ||
| 61 | 2000 + static_cast<u32>(error_code.module.Value()), | ||
| 62 | static_cast<u32>(error_code.description.Value()), info.unk8, info.unk7); | ||
| 63 | if (info.backtrace_size != 0x0) { | ||
| 64 | crash_report += "Registers:\n"; | ||
| 65 | // TODO(ogniK): This is just a guess, find a game which actually has non zero values | ||
| 66 | for (size_t i = 0; i < info.registers.size(); i++) { | ||
| 67 | crash_report += | ||
| 68 | fmt::format(" X[{:02d}]: {:016x}\n", i, info.registers[i]); | ||
| 69 | } | ||
| 70 | crash_report += fmt::format(" Unknown 0: {:016x}\n", info.unk0); | ||
| 71 | crash_report += fmt::format(" Unknown 1: {:016x}\n", info.unk1); | ||
| 72 | crash_report += fmt::format(" Unknown 2: {:016x}\n", info.unk2); | ||
| 73 | crash_report += fmt::format(" Unknown 3: {:016x}\n", info.unk3); | ||
| 74 | crash_report += fmt::format(" Unknown 4: {:016x}\n", info.unk4); | ||
| 75 | crash_report += fmt::format(" Unknown 5: {:016x}\n", info.unk5); | ||
| 76 | crash_report += fmt::format(" Unknown 6: {:016x}\n", info.unk6); | ||
| 77 | crash_report += "\nBacktrace:\n"; | ||
| 78 | for (size_t i = 0; i < info.backtrace_size; i++) { | ||
| 79 | crash_report += | ||
| 80 | fmt::format(" Backtrace[{:02d}]: {:016x}\n", i, info.backtrace[i]); | ||
| 81 | } | ||
| 82 | crash_report += fmt::format("\nUnknown 7: 0x{:016x}\n", info.unk7); | ||
| 83 | crash_report += fmt::format("Unknown 8: 0x{:016x}\n", info.unk8); | ||
| 84 | crash_report += fmt::format("Unknown 9: 0x{:016x}\n", info.unk9); | ||
| 85 | crash_report += fmt::format("Unknown 10: 0x{:016x}\n", info.unk10); | ||
| 86 | } | ||
| 87 | |||
| 88 | LOG_ERROR(Service_Fatal, "{}", crash_report); | ||
| 89 | |||
| 90 | const std::string crashreport_dir = | ||
| 91 | FileUtil::GetUserPath(FileUtil::UserPath::LogDir) + "crash_logs"; | ||
| 92 | |||
| 93 | if (!FileUtil::CreateFullPath(crashreport_dir)) { | ||
| 94 | LOG_ERROR( | ||
| 95 | Service_Fatal, | ||
| 96 | "Unable to create crash report directory. Possible log directory permissions issue."); | ||
| 97 | return; | ||
| 98 | } | ||
| 99 | |||
| 100 | const std::time_t t = std::time(nullptr); | ||
| 101 | const std::string crashreport_filename = | ||
| 102 | fmt::format("{}/{:016x}-{:%F-%H%M%S}.log", crashreport_dir, title_id, *std::localtime(&t)); | ||
| 103 | |||
| 104 | auto file = FileUtil::IOFile(crashreport_filename, "wb"); | ||
| 105 | if (file.IsOpen()) { | ||
| 106 | file.WriteString(crash_report); | ||
| 107 | LOG_ERROR(Service_Fatal, "Saving error report to {}", crashreport_filename); | ||
| 108 | } else { | ||
| 109 | LOG_ERROR(Service_Fatal, "Failed to save error report to {}", crashreport_filename); | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | static void ThrowFatalError(ResultCode error_code, FatalType fatal_type, const FatalInfo& info) { | ||
| 114 | LOG_ERROR(Service_Fatal, "Threw fatal error type {}", static_cast<u32>(fatal_type)); | ||
| 115 | switch (fatal_type) { | ||
| 116 | case FatalType::ErrorReportAndScreen: | ||
| 117 | GenerateErrorReport(error_code, info); | ||
| 118 | [[fallthrough]]; | ||
| 119 | case FatalType::ErrorScreen: | ||
| 120 | // Since we have no fatal:u error screen. We should just kill execution instead | ||
| 121 | ASSERT(false); | ||
| 122 | break; | ||
| 123 | // Should not throw a fatal screen but should generate an error report | ||
| 124 | case FatalType::ErrorReport: | ||
| 125 | GenerateErrorReport(error_code, info); | ||
| 126 | break; | ||
| 127 | }; | ||
| 128 | } | ||
| 129 | |||
| 130 | void Module::Interface::ThrowFatal(Kernel::HLERequestContext& ctx) { | ||
| 131 | LOG_ERROR(Service_Fatal, "called"); | ||
| 132 | IPC::RequestParser rp{ctx}; | ||
| 133 | auto error_code = rp.Pop<ResultCode>(); | ||
| 134 | |||
| 135 | ThrowFatalError(error_code, FatalType::ErrorScreen, {}); | ||
| 136 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 137 | rb.Push(RESULT_SUCCESS); | ||
| 138 | } | ||
| 139 | |||
| 18 | void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) { | 140 | void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) { |
| 141 | LOG_ERROR(Service_Fatal, "called"); | ||
| 19 | IPC::RequestParser rp(ctx); | 142 | IPC::RequestParser rp(ctx); |
| 20 | u32 error_code = rp.Pop<u32>(); | 143 | auto error_code = rp.Pop<ResultCode>(); |
| 21 | LOG_WARNING(Service_Fatal, "(STUBBED) called, error_code=0x{:X}", error_code); | 144 | auto fatal_type = rp.PopEnum<FatalType>(); |
| 145 | |||
| 146 | ThrowFatalError(error_code, fatal_type, {}); // No info is passed with ThrowFatalWithPolicy | ||
| 22 | IPC::ResponseBuilder rb{ctx, 2}; | 147 | IPC::ResponseBuilder rb{ctx, 2}; |
| 23 | rb.Push(RESULT_SUCCESS); | 148 | rb.Push(RESULT_SUCCESS); |
| 24 | } | 149 | } |
| 25 | 150 | ||
| 26 | void Module::Interface::ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx) { | 151 | void Module::Interface::ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx) { |
| 27 | LOG_WARNING(Service_Fatal, "(STUBBED) called"); | 152 | LOG_ERROR(Service_Fatal, "called"); |
| 153 | IPC::RequestParser rp(ctx); | ||
| 154 | auto error_code = rp.Pop<ResultCode>(); | ||
| 155 | auto fatal_type = rp.PopEnum<FatalType>(); | ||
| 156 | auto fatal_info = ctx.ReadBuffer(); | ||
| 157 | FatalInfo info{}; | ||
| 158 | |||
| 159 | ASSERT_MSG(fatal_info.size() == sizeof(FatalInfo), "Invalid fatal info buffer size!"); | ||
| 160 | std::memcpy(&info, fatal_info.data(), sizeof(FatalInfo)); | ||
| 161 | |||
| 162 | ThrowFatalError(error_code, fatal_type, info); | ||
| 28 | IPC::ResponseBuilder rb{ctx, 2}; | 163 | IPC::ResponseBuilder rb{ctx, 2}; |
| 29 | rb.Push(RESULT_SUCCESS); | 164 | rb.Push(RESULT_SUCCESS); |
| 30 | } | 165 | } |
diff --git a/src/core/hle/service/fatal/fatal.h b/src/core/hle/service/fatal/fatal.h index 4d9a5be52..09371ff7f 100644 --- a/src/core/hle/service/fatal/fatal.h +++ b/src/core/hle/service/fatal/fatal.h | |||
| @@ -15,6 +15,7 @@ public: | |||
| 15 | explicit Interface(std::shared_ptr<Module> module, const char* name); | 15 | explicit Interface(std::shared_ptr<Module> module, const char* name); |
| 16 | ~Interface() override; | 16 | ~Interface() override; |
| 17 | 17 | ||
| 18 | void ThrowFatal(Kernel::HLERequestContext& ctx); | ||
| 18 | void ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx); | 19 | void ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx); |
| 19 | void ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx); | 20 | void ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx); |
| 20 | 21 | ||
diff --git a/src/core/hle/service/fatal/fatal_u.cpp b/src/core/hle/service/fatal/fatal_u.cpp index befc307cf..1572a2051 100644 --- a/src/core/hle/service/fatal/fatal_u.cpp +++ b/src/core/hle/service/fatal/fatal_u.cpp | |||
| @@ -8,7 +8,7 @@ namespace Service::Fatal { | |||
| 8 | 8 | ||
| 9 | Fatal_U::Fatal_U(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "fatal:u") { | 9 | Fatal_U::Fatal_U(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "fatal:u") { |
| 10 | static const FunctionInfo functions[] = { | 10 | static const FunctionInfo functions[] = { |
| 11 | {0, nullptr, "ThrowFatal"}, | 11 | {0, &Fatal_U::ThrowFatal, "ThrowFatal"}, |
| 12 | {1, &Fatal_U::ThrowFatalWithPolicy, "ThrowFatalWithPolicy"}, | 12 | {1, &Fatal_U::ThrowFatalWithPolicy, "ThrowFatalWithPolicy"}, |
| 13 | {2, &Fatal_U::ThrowFatalWithCpuContext, "ThrowFatalWithCpuContext"}, | 13 | {2, &Fatal_U::ThrowFatalWithCpuContext, "ThrowFatalWithCpuContext"}, |
| 14 | }; | 14 | }; |
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index 5c4971724..aed2abb71 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp | |||
| @@ -197,7 +197,7 @@ ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const s | |||
| 197 | auto dir = GetDirectoryRelativeWrapped(backing, path); | 197 | auto dir = GetDirectoryRelativeWrapped(backing, path); |
| 198 | if (dir == nullptr) { | 198 | if (dir == nullptr) { |
| 199 | // TODO(DarkLordZach): Find a better error code for this | 199 | // TODO(DarkLordZach): Find a better error code for this |
| 200 | return ResultCode(-1); | 200 | return FileSys::ERROR_PATH_NOT_FOUND; |
| 201 | } | 201 | } |
| 202 | return MakeResult(dir); | 202 | return MakeResult(dir); |
| 203 | } | 203 | } |
| @@ -343,6 +343,15 @@ std::shared_ptr<FileSys::RegisteredCache> GetSDMCContents() { | |||
| 343 | return sdmc_factory->GetSDMCContents(); | 343 | return sdmc_factory->GetSDMCContents(); |
| 344 | } | 344 | } |
| 345 | 345 | ||
| 346 | FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) { | ||
| 347 | LOG_TRACE(Service_FS, "Opening mod load root for tid={:016X}", title_id); | ||
| 348 | |||
| 349 | if (bis_factory == nullptr) | ||
| 350 | return nullptr; | ||
| 351 | |||
| 352 | return bis_factory->GetModificationLoadRoot(title_id); | ||
| 353 | } | ||
| 354 | |||
| 346 | void CreateFactories(const FileSys::VirtualFilesystem& vfs, bool overwrite) { | 355 | void CreateFactories(const FileSys::VirtualFilesystem& vfs, bool overwrite) { |
| 347 | if (overwrite) { | 356 | if (overwrite) { |
| 348 | bis_factory = nullptr; | 357 | bis_factory = nullptr; |
| @@ -354,9 +363,11 @@ void CreateFactories(const FileSys::VirtualFilesystem& vfs, bool overwrite) { | |||
| 354 | FileSys::Mode::ReadWrite); | 363 | FileSys::Mode::ReadWrite); |
| 355 | auto sd_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), | 364 | auto sd_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), |
| 356 | FileSys::Mode::ReadWrite); | 365 | FileSys::Mode::ReadWrite); |
| 366 | auto load_directory = vfs->OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir), | ||
| 367 | FileSys::Mode::ReadWrite); | ||
| 357 | 368 | ||
| 358 | if (bis_factory == nullptr) | 369 | if (bis_factory == nullptr) |
| 359 | bis_factory = std::make_unique<FileSys::BISFactory>(nand_directory); | 370 | bis_factory = std::make_unique<FileSys::BISFactory>(nand_directory, load_directory); |
| 360 | if (save_data_factory == nullptr) | 371 | if (save_data_factory == nullptr) |
| 361 | save_data_factory = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory)); | 372 | save_data_factory = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory)); |
| 362 | if (sdmc_factory == nullptr) | 373 | if (sdmc_factory == nullptr) |
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h index aab65a2b8..7039a2247 100644 --- a/src/core/hle/service/filesystem/filesystem.h +++ b/src/core/hle/service/filesystem/filesystem.h | |||
| @@ -52,6 +52,8 @@ std::shared_ptr<FileSys::RegisteredCache> GetSystemNANDContents(); | |||
| 52 | std::shared_ptr<FileSys::RegisteredCache> GetUserNANDContents(); | 52 | std::shared_ptr<FileSys::RegisteredCache> GetUserNANDContents(); |
| 53 | std::shared_ptr<FileSys::RegisteredCache> GetSDMCContents(); | 53 | std::shared_ptr<FileSys::RegisteredCache> GetSDMCContents(); |
| 54 | 54 | ||
| 55 | FileSys::VirtualDir GetModificationLoadRoot(u64 title_id); | ||
| 56 | |||
| 55 | // Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function | 57 | // Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function |
| 56 | // above is called. | 58 | // above is called. |
| 57 | void CreateFactories(const FileSys::VirtualFilesystem& vfs, bool overwrite = true); | 59 | void CreateFactories(const FileSys::VirtualFilesystem& vfs, bool overwrite = true); |
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index a8e0c869f..7c6b0a4e6 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp | |||
| @@ -89,7 +89,7 @@ private: | |||
| 89 | controller_header.left_color_body = JOYCON_BODY_NEON_BLUE; | 89 | controller_header.left_color_body = JOYCON_BODY_NEON_BLUE; |
| 90 | controller_header.left_color_buttons = JOYCON_BUTTONS_NEON_BLUE; | 90 | controller_header.left_color_buttons = JOYCON_BUTTONS_NEON_BLUE; |
| 91 | 91 | ||
| 92 | for (size_t controller = 0; controller < mem.controllers.size(); controller++) { | 92 | for (std::size_t controller = 0; controller < mem.controllers.size(); controller++) { |
| 93 | for (auto& layout : mem.controllers[controller].layouts) { | 93 | for (auto& layout : mem.controllers[controller].layouts) { |
| 94 | layout.header.num_entries = HID_NUM_ENTRIES; | 94 | layout.header.num_entries = HID_NUM_ENTRIES; |
| 95 | layout.header.max_entry_index = HID_NUM_ENTRIES - 1; | 95 | layout.header.max_entry_index = HID_NUM_ENTRIES - 1; |
| @@ -313,7 +313,7 @@ public: | |||
| 313 | {64, nullptr, "DeactivateJoySixAxisSensor"}, | 313 | {64, nullptr, "DeactivateJoySixAxisSensor"}, |
| 314 | {65, nullptr, "GetJoySixAxisSensorLifoHandle"}, | 314 | {65, nullptr, "GetJoySixAxisSensorLifoHandle"}, |
| 315 | {66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"}, | 315 | {66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"}, |
| 316 | {67, nullptr, "StopSixAxisSensor"}, | 316 | {67, &Hid::StopSixAxisSensor, "StopSixAxisSensor"}, |
| 317 | {68, nullptr, "IsSixAxisSensorFusionEnabled"}, | 317 | {68, nullptr, "IsSixAxisSensorFusionEnabled"}, |
| 318 | {69, nullptr, "EnableSixAxisSensorFusion"}, | 318 | {69, nullptr, "EnableSixAxisSensorFusion"}, |
| 319 | {70, nullptr, "SetSixAxisSensorFusionParameters"}, | 319 | {70, nullptr, "SetSixAxisSensorFusionParameters"}, |
| @@ -329,7 +329,7 @@ public: | |||
| 329 | {80, nullptr, "GetGyroscopeZeroDriftMode"}, | 329 | {80, nullptr, "GetGyroscopeZeroDriftMode"}, |
| 330 | {81, nullptr, "ResetGyroscopeZeroDriftMode"}, | 330 | {81, nullptr, "ResetGyroscopeZeroDriftMode"}, |
| 331 | {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"}, | 331 | {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"}, |
| 332 | {91, nullptr, "ActivateGesture"}, | 332 | {91, &Hid::ActivateGesture, "ActivateGesture"}, |
| 333 | {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"}, | 333 | {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"}, |
| 334 | {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"}, | 334 | {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"}, |
| 335 | {102, &Hid::SetSupportedNpadIdType, "SetSupportedNpadIdType"}, | 335 | {102, &Hid::SetSupportedNpadIdType, "SetSupportedNpadIdType"}, |
| @@ -338,7 +338,7 @@ public: | |||
| 338 | {106, &Hid::AcquireNpadStyleSetUpdateEventHandle, "AcquireNpadStyleSetUpdateEventHandle"}, | 338 | {106, &Hid::AcquireNpadStyleSetUpdateEventHandle, "AcquireNpadStyleSetUpdateEventHandle"}, |
| 339 | {107, &Hid::DisconnectNpad, "DisconnectNpad"}, | 339 | {107, &Hid::DisconnectNpad, "DisconnectNpad"}, |
| 340 | {108, &Hid::GetPlayerLedPattern, "GetPlayerLedPattern"}, | 340 | {108, &Hid::GetPlayerLedPattern, "GetPlayerLedPattern"}, |
| 341 | {109, nullptr, "ActivateNpadWithRevision"}, | 341 | {109, &Hid::ActivateNpadWithRevision, "ActivateNpadWithRevision"}, |
| 342 | {120, &Hid::SetNpadJoyHoldType, "SetNpadJoyHoldType"}, | 342 | {120, &Hid::SetNpadJoyHoldType, "SetNpadJoyHoldType"}, |
| 343 | {121, &Hid::GetNpadJoyHoldType, "GetNpadJoyHoldType"}, | 343 | {121, &Hid::GetNpadJoyHoldType, "GetNpadJoyHoldType"}, |
| 344 | {122, &Hid::SetNpadJoyAssignmentModeSingleByDefault, "SetNpadJoyAssignmentModeSingleByDefault"}, | 344 | {122, &Hid::SetNpadJoyAssignmentModeSingleByDefault, "SetNpadJoyAssignmentModeSingleByDefault"}, |
| @@ -364,8 +364,8 @@ public: | |||
| 364 | {208, nullptr, "GetActualVibrationGcErmCommand"}, | 364 | {208, nullptr, "GetActualVibrationGcErmCommand"}, |
| 365 | {209, nullptr, "BeginPermitVibrationSession"}, | 365 | {209, nullptr, "BeginPermitVibrationSession"}, |
| 366 | {210, nullptr, "EndPermitVibrationSession"}, | 366 | {210, nullptr, "EndPermitVibrationSession"}, |
| 367 | {300, nullptr, "ActivateConsoleSixAxisSensor"}, | 367 | {300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"}, |
| 368 | {301, nullptr, "StartConsoleSixAxisSensor"}, | 368 | {301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"}, |
| 369 | {302, nullptr, "StopConsoleSixAxisSensor"}, | 369 | {302, nullptr, "StopConsoleSixAxisSensor"}, |
| 370 | {303, nullptr, "ActivateSevenSixAxisSensor"}, | 370 | {303, nullptr, "ActivateSevenSixAxisSensor"}, |
| 371 | {304, nullptr, "StartSevenSixAxisSensor"}, | 371 | {304, nullptr, "StartSevenSixAxisSensor"}, |
| @@ -579,6 +579,36 @@ private: | |||
| 579 | rb.Push(RESULT_SUCCESS); | 579 | rb.Push(RESULT_SUCCESS); |
| 580 | LOG_WARNING(Service_HID, "(STUBBED) called"); | 580 | LOG_WARNING(Service_HID, "(STUBBED) called"); |
| 581 | } | 581 | } |
| 582 | |||
| 583 | void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { | ||
| 584 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 585 | rb.Push(RESULT_SUCCESS); | ||
| 586 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 587 | } | ||
| 588 | |||
| 589 | void StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) { | ||
| 590 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 591 | rb.Push(RESULT_SUCCESS); | ||
| 592 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 593 | } | ||
| 594 | |||
| 595 | void StopSixAxisSensor(Kernel::HLERequestContext& ctx) { | ||
| 596 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 597 | rb.Push(RESULT_SUCCESS); | ||
| 598 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 599 | } | ||
| 600 | |||
| 601 | void ActivateGesture(Kernel::HLERequestContext& ctx) { | ||
| 602 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 603 | rb.Push(RESULT_SUCCESS); | ||
| 604 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 605 | } | ||
| 606 | |||
| 607 | void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) { | ||
| 608 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 609 | rb.Push(RESULT_SUCCESS); | ||
| 610 | LOG_WARNING(Service_HID, "(STUBBED) called"); | ||
| 611 | } | ||
| 582 | }; | 612 | }; |
| 583 | 613 | ||
| 584 | class HidDbg final : public ServiceFramework<HidDbg> { | 614 | class HidDbg final : public ServiceFramework<HidDbg> { |
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp index e587ad0d8..872e3c344 100644 --- a/src/core/hle/service/hid/irs.cpp +++ b/src/core/hle/service/hid/irs.cpp | |||
| @@ -2,6 +2,11 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/swap.h" | ||
| 6 | #include "core/core.h" | ||
| 7 | #include "core/core_timing.h" | ||
| 8 | #include "core/hle/ipc_helpers.h" | ||
| 9 | #include "core/hle/kernel/shared_memory.h" | ||
| 5 | #include "core/hle/service/hid/irs.h" | 10 | #include "core/hle/service/hid/irs.h" |
| 6 | 11 | ||
| 7 | namespace Service::HID { | 12 | namespace Service::HID { |
| @@ -9,28 +14,145 @@ namespace Service::HID { | |||
| 9 | IRS::IRS() : ServiceFramework{"irs"} { | 14 | IRS::IRS() : ServiceFramework{"irs"} { |
| 10 | // clang-format off | 15 | // clang-format off |
| 11 | static const FunctionInfo functions[] = { | 16 | static const FunctionInfo functions[] = { |
| 12 | {302, nullptr, "ActivateIrsensor"}, | 17 | {302, &IRS::ActivateIrsensor, "ActivateIrsensor"}, |
| 13 | {303, nullptr, "DeactivateIrsensor"}, | 18 | {303, &IRS::DeactivateIrsensor, "DeactivateIrsensor"}, |
| 14 | {304, nullptr, "GetIrsensorSharedMemoryHandle"}, | 19 | {304, &IRS::GetIrsensorSharedMemoryHandle, "GetIrsensorSharedMemoryHandle"}, |
| 15 | {305, nullptr, "StopImageProcessor"}, | 20 | {305, &IRS::StopImageProcessor, "StopImageProcessor"}, |
| 16 | {306, nullptr, "RunMomentProcessor"}, | 21 | {306, &IRS::RunMomentProcessor, "RunMomentProcessor"}, |
| 17 | {307, nullptr, "RunClusteringProcessor"}, | 22 | {307, &IRS::RunClusteringProcessor, "RunClusteringProcessor"}, |
| 18 | {308, nullptr, "RunImageTransferProcessor"}, | 23 | {308, &IRS::RunImageTransferProcessor, "RunImageTransferProcessor"}, |
| 19 | {309, nullptr, "GetImageTransferProcessorState"}, | 24 | {309, &IRS::GetImageTransferProcessorState, "GetImageTransferProcessorState"}, |
| 20 | {310, nullptr, "RunTeraPluginProcessor"}, | 25 | {310, &IRS::RunTeraPluginProcessor, "RunTeraPluginProcessor"}, |
| 21 | {311, nullptr, "GetNpadIrCameraHandle"}, | 26 | {311, &IRS::GetNpadIrCameraHandle, "GetNpadIrCameraHandle"}, |
| 22 | {312, nullptr, "RunPointingProcessor"}, | 27 | {312, &IRS::RunPointingProcessor, "RunPointingProcessor"}, |
| 23 | {313, nullptr, "SuspendImageProcessor"}, | 28 | {313, &IRS::SuspendImageProcessor, "SuspendImageProcessor"}, |
| 24 | {314, nullptr, "CheckFirmwareVersion"}, | 29 | {314, &IRS::CheckFirmwareVersion, "CheckFirmwareVersion"}, |
| 25 | {315, nullptr, "SetFunctionLevel"}, | 30 | {315, &IRS::SetFunctionLevel, "SetFunctionLevel"}, |
| 26 | {316, nullptr, "RunImageTransferExProcessor"}, | 31 | {316, &IRS::RunImageTransferExProcessor, "RunImageTransferExProcessor"}, |
| 27 | {317, nullptr, "RunIrLedProcessor"}, | 32 | {317, &IRS::RunIrLedProcessor, "RunIrLedProcessor"}, |
| 28 | {318, nullptr, "StopImageProcessorAsync"}, | 33 | {318, &IRS::StopImageProcessorAsync, "StopImageProcessorAsync"}, |
| 29 | {319, nullptr, "ActivateIrsensorWithFunctionLevel"}, | 34 | {319, &IRS::ActivateIrsensorWithFunctionLevel, "ActivateIrsensorWithFunctionLevel"}, |
| 30 | }; | 35 | }; |
| 31 | // clang-format on | 36 | // clang-format on |
| 32 | 37 | ||
| 33 | RegisterHandlers(functions); | 38 | RegisterHandlers(functions); |
| 39 | |||
| 40 | auto& kernel = Core::System::GetInstance().Kernel(); | ||
| 41 | shared_mem = Kernel::SharedMemory::Create( | ||
| 42 | kernel, nullptr, 0x8000, Kernel::MemoryPermission::ReadWrite, | ||
| 43 | Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "IRS:SharedMemory"); | ||
| 44 | } | ||
| 45 | |||
| 46 | void IRS::ActivateIrsensor(Kernel::HLERequestContext& ctx) { | ||
| 47 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 48 | rb.Push(RESULT_SUCCESS); | ||
| 49 | LOG_WARNING(Service_IRS, "(STUBBED) called"); | ||
| 50 | } | ||
| 51 | |||
| 52 | void IRS::DeactivateIrsensor(Kernel::HLERequestContext& ctx) { | ||
| 53 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 54 | rb.Push(RESULT_SUCCESS); | ||
| 55 | LOG_WARNING(Service_IRS, "(STUBBED) called"); | ||
| 56 | } | ||
| 57 | |||
| 58 | void IRS::GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx) { | ||
| 59 | IPC::ResponseBuilder rb{ctx, 2, 1}; | ||
| 60 | rb.Push(RESULT_SUCCESS); | ||
| 61 | rb.PushCopyObjects(shared_mem); | ||
| 62 | LOG_DEBUG(Service_IRS, "called"); | ||
| 63 | } | ||
| 64 | |||
| 65 | void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) { | ||
| 66 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 67 | rb.Push(RESULT_SUCCESS); | ||
| 68 | LOG_WARNING(Service_IRS, "(STUBBED) called"); | ||
| 69 | } | ||
| 70 | |||
| 71 | void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) { | ||
| 72 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 73 | rb.Push(RESULT_SUCCESS); | ||
| 74 | LOG_WARNING(Service_IRS, "(STUBBED) called"); | ||
| 75 | } | ||
| 76 | |||
| 77 | void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) { | ||
| 78 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 79 | rb.Push(RESULT_SUCCESS); | ||
| 80 | LOG_WARNING(Service_IRS, "(STUBBED) called"); | ||
| 81 | } | ||
| 82 | |||
| 83 | void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) { | ||
| 84 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 85 | rb.Push(RESULT_SUCCESS); | ||
| 86 | LOG_WARNING(Service_IRS, "(STUBBED) called"); | ||
| 87 | } | ||
| 88 | |||
| 89 | void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) { | ||
| 90 | IPC::ResponseBuilder rb{ctx, 5}; | ||
| 91 | rb.Push(RESULT_SUCCESS); | ||
| 92 | rb.PushRaw<u64>(CoreTiming::GetTicks()); | ||
| 93 | rb.PushRaw<u32>(0); | ||
| 94 | LOG_WARNING(Service_IRS, "(STUBBED) called"); | ||
| 95 | } | ||
| 96 | |||
| 97 | void IRS::RunTeraPluginProcessor(Kernel::HLERequestContext& ctx) { | ||
| 98 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 99 | rb.Push(RESULT_SUCCESS); | ||
| 100 | LOG_WARNING(Service_IRS, "(STUBBED) called"); | ||
| 101 | } | ||
| 102 | |||
| 103 | void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) { | ||
| 104 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 105 | rb.Push(RESULT_SUCCESS); | ||
| 106 | rb.PushRaw<u32>(device_handle); | ||
| 107 | LOG_WARNING(Service_IRS, "(STUBBED) called"); | ||
| 108 | } | ||
| 109 | |||
| 110 | void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) { | ||
| 111 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 112 | rb.Push(RESULT_SUCCESS); | ||
| 113 | LOG_WARNING(Service_IRS, "(STUBBED) called"); | ||
| 114 | } | ||
| 115 | |||
| 116 | void IRS::SuspendImageProcessor(Kernel::HLERequestContext& ctx) { | ||
| 117 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 118 | rb.Push(RESULT_SUCCESS); | ||
| 119 | LOG_WARNING(Service_IRS, "(STUBBED) called"); | ||
| 120 | } | ||
| 121 | |||
| 122 | void IRS::CheckFirmwareVersion(Kernel::HLERequestContext& ctx) { | ||
| 123 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 124 | rb.Push(RESULT_SUCCESS); | ||
| 125 | LOG_WARNING(Service_IRS, "(STUBBED) called"); | ||
| 126 | } | ||
| 127 | |||
| 128 | void IRS::SetFunctionLevel(Kernel::HLERequestContext& ctx) { | ||
| 129 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 130 | rb.Push(RESULT_SUCCESS); | ||
| 131 | LOG_WARNING(Service_IRS, "(STUBBED) called"); | ||
| 132 | } | ||
| 133 | |||
| 134 | void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) { | ||
| 135 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 136 | rb.Push(RESULT_SUCCESS); | ||
| 137 | LOG_WARNING(Service_IRS, "(STUBBED) called"); | ||
| 138 | } | ||
| 139 | |||
| 140 | void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) { | ||
| 141 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 142 | rb.Push(RESULT_SUCCESS); | ||
| 143 | LOG_WARNING(Service_IRS, "(STUBBED) called"); | ||
| 144 | } | ||
| 145 | |||
| 146 | void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) { | ||
| 147 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 148 | rb.Push(RESULT_SUCCESS); | ||
| 149 | LOG_WARNING(Service_IRS, "(STUBBED) called"); | ||
| 150 | } | ||
| 151 | |||
| 152 | void IRS::ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx) { | ||
| 153 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 154 | rb.Push(RESULT_SUCCESS); | ||
| 155 | LOG_WARNING(Service_IRS, "(STUBBED) called"); | ||
| 34 | } | 156 | } |
| 35 | 157 | ||
| 36 | IRS::~IRS() = default; | 158 | IRS::~IRS() = default; |
diff --git a/src/core/hle/service/hid/irs.h b/src/core/hle/service/hid/irs.h index 6fb16b45d..12de6bfb3 100644 --- a/src/core/hle/service/hid/irs.h +++ b/src/core/hle/service/hid/irs.h | |||
| @@ -4,14 +4,41 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "core/hle/kernel/object.h" | ||
| 7 | #include "core/hle/service/service.h" | 8 | #include "core/hle/service/service.h" |
| 8 | 9 | ||
| 10 | namespace Kernel { | ||
| 11 | class SharedMemory; | ||
| 12 | } | ||
| 13 | |||
| 9 | namespace Service::HID { | 14 | namespace Service::HID { |
| 10 | 15 | ||
| 11 | class IRS final : public ServiceFramework<IRS> { | 16 | class IRS final : public ServiceFramework<IRS> { |
| 12 | public: | 17 | public: |
| 13 | explicit IRS(); | 18 | explicit IRS(); |
| 14 | ~IRS() override; | 19 | ~IRS() override; |
| 20 | |||
| 21 | private: | ||
| 22 | void ActivateIrsensor(Kernel::HLERequestContext& ctx); | ||
| 23 | void DeactivateIrsensor(Kernel::HLERequestContext& ctx); | ||
| 24 | void GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx); | ||
| 25 | void StopImageProcessor(Kernel::HLERequestContext& ctx); | ||
| 26 | void RunMomentProcessor(Kernel::HLERequestContext& ctx); | ||
| 27 | void RunClusteringProcessor(Kernel::HLERequestContext& ctx); | ||
| 28 | void RunImageTransferProcessor(Kernel::HLERequestContext& ctx); | ||
| 29 | void GetImageTransferProcessorState(Kernel::HLERequestContext& ctx); | ||
| 30 | void RunTeraPluginProcessor(Kernel::HLERequestContext& ctx); | ||
| 31 | void GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx); | ||
| 32 | void RunPointingProcessor(Kernel::HLERequestContext& ctx); | ||
| 33 | void SuspendImageProcessor(Kernel::HLERequestContext& ctx); | ||
| 34 | void CheckFirmwareVersion(Kernel::HLERequestContext& ctx); | ||
| 35 | void SetFunctionLevel(Kernel::HLERequestContext& ctx); | ||
| 36 | void RunImageTransferExProcessor(Kernel::HLERequestContext& ctx); | ||
| 37 | void RunIrLedProcessor(Kernel::HLERequestContext& ctx); | ||
| 38 | void StopImageProcessorAsync(Kernel::HLERequestContext& ctx); | ||
| 39 | void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx); | ||
| 40 | Kernel::SharedPtr<Kernel::SharedMemory> shared_mem; | ||
| 41 | const u32 device_handle{0xABCD}; | ||
| 15 | }; | 42 | }; |
| 16 | 43 | ||
| 17 | class IRS_SYS final : public ServiceFramework<IRS_SYS> { | 44 | class IRS_SYS final : public ServiceFramework<IRS_SYS> { |
diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp index 098da2a41..c89157a4d 100644 --- a/src/core/hle/service/lm/lm.cpp +++ b/src/core/hle/service/lm/lm.cpp | |||
| @@ -99,7 +99,7 @@ private: | |||
| 99 | std::string thread; | 99 | std::string thread; |
| 100 | while (addr < end_addr) { | 100 | while (addr < end_addr) { |
| 101 | const Field field{static_cast<Field>(Memory::Read8(addr++))}; | 101 | const Field field{static_cast<Field>(Memory::Read8(addr++))}; |
| 102 | const size_t length{Memory::Read8(addr++)}; | 102 | const std::size_t length{Memory::Read8(addr++)}; |
| 103 | 103 | ||
| 104 | if (static_cast<Field>(Memory::Read8(addr)) == Field::Skip) { | 104 | if (static_cast<Field>(Memory::Read8(addr)) == Field::Skip) { |
| 105 | ++addr; | 105 | ++addr; |
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp index f8d2127d9..8c07a05c2 100644 --- a/src/core/hle/service/nfp/nfp.cpp +++ b/src/core/hle/service/nfp/nfp.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/logging/log.h" | 5 | #include "common/logging/log.h" |
| 6 | #include "core/core.h" | ||
| 6 | #include "core/hle/ipc_helpers.h" | 7 | #include "core/hle/ipc_helpers.h" |
| 7 | #include "core/hle/kernel/event.h" | 8 | #include "core/hle/kernel/event.h" |
| 8 | #include "core/hle/service/hid/hid.h" | 9 | #include "core/hle/service/hid/hid.h" |
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index ed4f5f539..10611ed6a 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp | |||
| @@ -31,7 +31,7 @@ public: | |||
| 31 | {1, &IRequest::GetResult, "GetResult"}, | 31 | {1, &IRequest::GetResult, "GetResult"}, |
| 32 | {2, &IRequest::GetSystemEventReadableHandles, "GetSystemEventReadableHandles"}, | 32 | {2, &IRequest::GetSystemEventReadableHandles, "GetSystemEventReadableHandles"}, |
| 33 | {3, &IRequest::Cancel, "Cancel"}, | 33 | {3, &IRequest::Cancel, "Cancel"}, |
| 34 | {4, nullptr, "Submit"}, | 34 | {4, &IRequest::Submit, "Submit"}, |
| 35 | {5, nullptr, "SetRequirement"}, | 35 | {5, nullptr, "SetRequirement"}, |
| 36 | {6, nullptr, "SetRequirementPreset"}, | 36 | {6, nullptr, "SetRequirementPreset"}, |
| 37 | {8, nullptr, "SetPriority"}, | 37 | {8, nullptr, "SetPriority"}, |
| @@ -61,6 +61,12 @@ public: | |||
| 61 | } | 61 | } |
| 62 | 62 | ||
| 63 | private: | 63 | private: |
| 64 | void Submit(Kernel::HLERequestContext& ctx) { | ||
| 65 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); | ||
| 66 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 67 | rb.Push(RESULT_SUCCESS); | ||
| 68 | } | ||
| 69 | |||
| 64 | void GetRequestState(Kernel::HLERequestContext& ctx) { | 70 | void GetRequestState(Kernel::HLERequestContext& ctx) { |
| 65 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); | 71 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); |
| 66 | IPC::ResponseBuilder rb{ctx, 3}; | 72 | IPC::ResponseBuilder rb{ctx, 3}; |
| @@ -114,10 +120,11 @@ public: | |||
| 114 | 120 | ||
| 115 | private: | 121 | private: |
| 116 | void GetClientId(Kernel::HLERequestContext& ctx) { | 122 | void GetClientId(Kernel::HLERequestContext& ctx) { |
| 123 | static constexpr u32 client_id = 1; | ||
| 117 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); | 124 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); |
| 118 | IPC::ResponseBuilder rb{ctx, 4}; | 125 | IPC::ResponseBuilder rb{ctx, 4}; |
| 119 | rb.Push(RESULT_SUCCESS); | 126 | rb.Push(RESULT_SUCCESS); |
| 120 | rb.Push<u64>(0); | 127 | rb.Push<u64>(client_id); // Client ID needs to be non zero otherwise it's considered invalid |
| 121 | } | 128 | } |
| 122 | void CreateScanRequest(Kernel::HLERequestContext& ctx) { | 129 | void CreateScanRequest(Kernel::HLERequestContext& ctx) { |
| 123 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 130 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| @@ -141,10 +148,16 @@ private: | |||
| 141 | rb.Push(RESULT_SUCCESS); | 148 | rb.Push(RESULT_SUCCESS); |
| 142 | } | 149 | } |
| 143 | void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) { | 150 | void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) { |
| 144 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 151 | ASSERT_MSG(ctx.GetReadBufferSize() == 0x17c, "NetworkProfileData is not the correct size"); |
| 152 | u128 uuid{}; | ||
| 153 | auto buffer = ctx.ReadBuffer(); | ||
| 154 | std::memcpy(&uuid, buffer.data() + 8, sizeof(u128)); | ||
| 155 | |||
| 156 | IPC::ResponseBuilder rb{ctx, 6, 0, 1}; | ||
| 145 | 157 | ||
| 146 | rb.Push(RESULT_SUCCESS); | 158 | rb.Push(RESULT_SUCCESS); |
| 147 | rb.PushIpcInterface<INetworkProfile>(); | 159 | rb.PushIpcInterface<INetworkProfile>(); |
| 160 | rb.PushRaw<u128>(uuid); | ||
| 148 | 161 | ||
| 149 | LOG_DEBUG(Service_NIFM, "called"); | 162 | LOG_DEBUG(Service_NIFM, "called"); |
| 150 | } | 163 | } |
diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp index bd05b0a70..261ad539c 100644 --- a/src/core/hle/service/nim/nim.cpp +++ b/src/core/hle/service/nim/nim.cpp | |||
| @@ -2,6 +2,11 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <chrono> | ||
| 6 | #include <ctime> | ||
| 7 | #include "core/core.h" | ||
| 8 | #include "core/hle/ipc_helpers.h" | ||
| 9 | #include "core/hle/kernel/event.h" | ||
| 5 | #include "core/hle/service/nim/nim.h" | 10 | #include "core/hle/service/nim/nim.h" |
| 6 | #include "core/hle/service/service.h" | 11 | #include "core/hle/service/service.h" |
| 7 | #include "core/hle/service/sm/sm.h" | 12 | #include "core/hle/service/sm/sm.h" |
| @@ -100,19 +105,111 @@ public: | |||
| 100 | } | 105 | } |
| 101 | }; | 106 | }; |
| 102 | 107 | ||
| 108 | class IEnsureNetworkClockAvailabilityService final | ||
| 109 | : public ServiceFramework<IEnsureNetworkClockAvailabilityService> { | ||
| 110 | public: | ||
| 111 | IEnsureNetworkClockAvailabilityService() | ||
| 112 | : ServiceFramework("IEnsureNetworkClockAvailabilityService") { | ||
| 113 | static const FunctionInfo functions[] = { | ||
| 114 | {0, &IEnsureNetworkClockAvailabilityService::StartTask, "StartTask"}, | ||
| 115 | {1, &IEnsureNetworkClockAvailabilityService::GetFinishNotificationEvent, | ||
| 116 | "GetFinishNotificationEvent"}, | ||
| 117 | {2, &IEnsureNetworkClockAvailabilityService::GetResult, "GetResult"}, | ||
| 118 | {3, &IEnsureNetworkClockAvailabilityService::Cancel, "Cancel"}, | ||
| 119 | {4, &IEnsureNetworkClockAvailabilityService::IsProcessing, "IsProcessing"}, | ||
| 120 | {5, &IEnsureNetworkClockAvailabilityService::GetServerTime, "GetServerTime"}, | ||
| 121 | }; | ||
| 122 | RegisterHandlers(functions); | ||
| 123 | |||
| 124 | auto& kernel = Core::System::GetInstance().Kernel(); | ||
| 125 | finished_event = | ||
| 126 | Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, | ||
| 127 | "IEnsureNetworkClockAvailabilityService:FinishEvent"); | ||
| 128 | } | ||
| 129 | |||
| 130 | private: | ||
| 131 | Kernel::SharedPtr<Kernel::Event> finished_event; | ||
| 132 | |||
| 133 | void StartTask(Kernel::HLERequestContext& ctx) { | ||
| 134 | // No need to connect to the internet, just finish the task straight away. | ||
| 135 | finished_event->Signal(); | ||
| 136 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 137 | rb.Push(RESULT_SUCCESS); | ||
| 138 | LOG_DEBUG(Service_NIM, "called"); | ||
| 139 | } | ||
| 140 | |||
| 141 | void GetFinishNotificationEvent(Kernel::HLERequestContext& ctx) { | ||
| 142 | IPC::ResponseBuilder rb{ctx, 2, 1}; | ||
| 143 | rb.Push(RESULT_SUCCESS); | ||
| 144 | rb.PushCopyObjects(finished_event); | ||
| 145 | LOG_DEBUG(Service_NIM, "called"); | ||
| 146 | } | ||
| 147 | |||
| 148 | void GetResult(Kernel::HLERequestContext& ctx) { | ||
| 149 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 150 | rb.Push(RESULT_SUCCESS); | ||
| 151 | LOG_DEBUG(Service_NIM, "called"); | ||
| 152 | } | ||
| 153 | |||
| 154 | void Cancel(Kernel::HLERequestContext& ctx) { | ||
| 155 | finished_event->Clear(); | ||
| 156 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 157 | rb.Push(RESULT_SUCCESS); | ||
| 158 | LOG_DEBUG(Service_NIM, "called"); | ||
| 159 | } | ||
| 160 | |||
| 161 | void IsProcessing(Kernel::HLERequestContext& ctx) { | ||
| 162 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 163 | rb.Push(RESULT_SUCCESS); | ||
| 164 | rb.PushRaw<u32>(0); // We instantly process the request | ||
| 165 | LOG_DEBUG(Service_NIM, "called"); | ||
| 166 | } | ||
| 167 | |||
| 168 | void GetServerTime(Kernel::HLERequestContext& ctx) { | ||
| 169 | const s64 server_time{std::chrono::duration_cast<std::chrono::seconds>( | ||
| 170 | std::chrono::system_clock::now().time_since_epoch()) | ||
| 171 | .count()}; | ||
| 172 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 173 | rb.Push(RESULT_SUCCESS); | ||
| 174 | rb.PushRaw<s64>(server_time); | ||
| 175 | LOG_DEBUG(Service_NIM, "called"); | ||
| 176 | } | ||
| 177 | }; | ||
| 178 | |||
| 103 | class NTC final : public ServiceFramework<NTC> { | 179 | class NTC final : public ServiceFramework<NTC> { |
| 104 | public: | 180 | public: |
| 105 | explicit NTC() : ServiceFramework{"ntc"} { | 181 | explicit NTC() : ServiceFramework{"ntc"} { |
| 106 | // clang-format off | 182 | // clang-format off |
| 107 | static const FunctionInfo functions[] = { | 183 | static const FunctionInfo functions[] = { |
| 108 | {0, nullptr, "OpenEnsureNetworkClockAvailabilityService"}, | 184 | {0, &NTC::OpenEnsureNetworkClockAvailabilityService, "OpenEnsureNetworkClockAvailabilityService"}, |
| 109 | {100, nullptr, "SuspendAutonomicTimeCorrection"}, | 185 | {100, &NTC::SuspendAutonomicTimeCorrection, "SuspendAutonomicTimeCorrection"}, |
| 110 | {101, nullptr, "ResumeAutonomicTimeCorrection"}, | 186 | {101, &NTC::ResumeAutonomicTimeCorrection, "ResumeAutonomicTimeCorrection"}, |
| 111 | }; | 187 | }; |
| 112 | // clang-format on | 188 | // clang-format on |
| 113 | 189 | ||
| 114 | RegisterHandlers(functions); | 190 | RegisterHandlers(functions); |
| 115 | } | 191 | } |
| 192 | |||
| 193 | private: | ||
| 194 | void OpenEnsureNetworkClockAvailabilityService(Kernel::HLERequestContext& ctx) { | ||
| 195 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 196 | rb.Push(RESULT_SUCCESS); | ||
| 197 | rb.PushIpcInterface<IEnsureNetworkClockAvailabilityService>(); | ||
| 198 | LOG_DEBUG(Service_NIM, "called"); | ||
| 199 | } | ||
| 200 | |||
| 201 | // TODO(ogniK): Do we need these? | ||
| 202 | void SuspendAutonomicTimeCorrection(Kernel::HLERequestContext& ctx) { | ||
| 203 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 204 | rb.Push(RESULT_SUCCESS); | ||
| 205 | LOG_WARNING(Service_NIM, "(STUBBED) called"); | ||
| 206 | } | ||
| 207 | |||
| 208 | void ResumeAutonomicTimeCorrection(Kernel::HLERequestContext& ctx) { | ||
| 209 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 210 | rb.Push(RESULT_SUCCESS); | ||
| 211 | LOG_WARNING(Service_NIM, "(STUBBED) called"); | ||
| 212 | } | ||
| 116 | }; | 213 | }; |
| 117 | 214 | ||
| 118 | void InstallInterfaces(SM::ServiceManager& sm) { | 215 | void InstallInterfaces(SM::ServiceManager& sm) { |
diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp index 447689a1a..4b2f758a8 100644 --- a/src/core/hle/service/ns/pl_u.cpp +++ b/src/core/hle/service/ns/pl_u.cpp | |||
| @@ -78,7 +78,7 @@ enum class LoadState : u32 { | |||
| 78 | }; | 78 | }; |
| 79 | 79 | ||
| 80 | static void DecryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output, | 80 | static void DecryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output, |
| 81 | size_t& offset) { | 81 | std::size_t& offset) { |
| 82 | ASSERT_MSG(offset + (input.size() * sizeof(u32)) < SHARED_FONT_MEM_SIZE, | 82 | ASSERT_MSG(offset + (input.size() * sizeof(u32)) < SHARED_FONT_MEM_SIZE, |
| 83 | "Shared fonts exceeds 17mb!"); | 83 | "Shared fonts exceeds 17mb!"); |
| 84 | ASSERT_MSG(input[0] == EXPECTED_MAGIC, "Failed to derive key, unexpected magic number"); | 84 | ASSERT_MSG(input[0] == EXPECTED_MAGIC, "Failed to derive key, unexpected magic number"); |
| @@ -95,7 +95,7 @@ static void DecryptSharedFont(const std::vector<u32>& input, std::vector<u8>& ou | |||
| 95 | } | 95 | } |
| 96 | 96 | ||
| 97 | static void EncryptSharedFont(const std::vector<u8>& input, std::vector<u8>& output, | 97 | static void EncryptSharedFont(const std::vector<u8>& input, std::vector<u8>& output, |
| 98 | size_t& offset) { | 98 | std::size_t& offset) { |
| 99 | ASSERT_MSG(offset + input.size() + 8 < SHARED_FONT_MEM_SIZE, "Shared fonts exceeds 17mb!"); | 99 | ASSERT_MSG(offset + input.size() + 8 < SHARED_FONT_MEM_SIZE, "Shared fonts exceeds 17mb!"); |
| 100 | const u32 KEY = EXPECTED_MAGIC ^ EXPECTED_RESULT; | 100 | const u32 KEY = EXPECTED_MAGIC ^ EXPECTED_RESULT; |
| 101 | std::memcpy(output.data() + offset, &EXPECTED_RESULT, sizeof(u32)); // Magic header | 101 | std::memcpy(output.data() + offset, &EXPECTED_RESULT, sizeof(u32)); // Magic header |
| @@ -113,7 +113,7 @@ static u32 GetU32Swapped(const u8* data) { | |||
| 113 | } | 113 | } |
| 114 | 114 | ||
| 115 | struct PL_U::Impl { | 115 | struct PL_U::Impl { |
| 116 | const FontRegion& GetSharedFontRegion(size_t index) const { | 116 | const FontRegion& GetSharedFontRegion(std::size_t index) const { |
| 117 | if (index >= shared_font_regions.size() || shared_font_regions.empty()) { | 117 | if (index >= shared_font_regions.size() || shared_font_regions.empty()) { |
| 118 | // No font fallback | 118 | // No font fallback |
| 119 | return EMPTY_REGION; | 119 | return EMPTY_REGION; |
| @@ -126,7 +126,7 @@ struct PL_U::Impl { | |||
| 126 | // based on the shared memory dump | 126 | // based on the shared memory dump |
| 127 | unsigned cur_offset = 0; | 127 | unsigned cur_offset = 0; |
| 128 | 128 | ||
| 129 | for (size_t i = 0; i < SHARED_FONTS.size(); i++) { | 129 | for (std::size_t i = 0; i < SHARED_FONTS.size(); i++) { |
| 130 | // Out of shared fonts/invalid font | 130 | // Out of shared fonts/invalid font |
| 131 | if (GetU32Swapped(input.data() + cur_offset) != EXPECTED_RESULT) { | 131 | if (GetU32Swapped(input.data() + cur_offset) != EXPECTED_RESULT) { |
| 132 | break; | 132 | break; |
| @@ -162,7 +162,7 @@ PL_U::PL_U() : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()} { | |||
| 162 | RegisterHandlers(functions); | 162 | RegisterHandlers(functions); |
| 163 | // Attempt to load shared font data from disk | 163 | // Attempt to load shared font data from disk |
| 164 | const auto nand = FileSystem::GetSystemNANDContents(); | 164 | const auto nand = FileSystem::GetSystemNANDContents(); |
| 165 | size_t offset = 0; | 165 | std::size_t offset = 0; |
| 166 | // Rebuild shared fonts from data ncas | 166 | // Rebuild shared fonts from data ncas |
| 167 | if (nand->HasEntry(static_cast<u64>(FontArchives::Standard), | 167 | if (nand->HasEntry(static_cast<u64>(FontArchives::Standard), |
| 168 | FileSys::ContentRecordType::Data)) { | 168 | FileSys::ContentRecordType::Data)) { |
| @@ -317,9 +317,9 @@ void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) { | |||
| 317 | 317 | ||
| 318 | void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { | 318 | void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { |
| 319 | // Map backing memory for the font data | 319 | // Map backing memory for the font data |
| 320 | Core::CurrentProcess()->vm_manager.MapMemoryBlock(SHARED_FONT_MEM_VADDR, impl->shared_font, 0, | 320 | Core::CurrentProcess()->VMManager().MapMemoryBlock(SHARED_FONT_MEM_VADDR, impl->shared_font, 0, |
| 321 | SHARED_FONT_MEM_SIZE, | 321 | SHARED_FONT_MEM_SIZE, |
| 322 | Kernel::MemoryState::Shared); | 322 | Kernel::MemoryState::Shared); |
| 323 | 323 | ||
| 324 | // Create shared font memory object | 324 | // Create shared font memory object |
| 325 | auto& kernel = Core::System::GetInstance().Kernel(); | 325 | auto& kernel = Core::System::GetInstance().Kernel(); |
| @@ -344,7 +344,7 @@ void PL_U::GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx) { | |||
| 344 | std::vector<u32> font_sizes; | 344 | std::vector<u32> font_sizes; |
| 345 | 345 | ||
| 346 | // TODO(ogniK): Have actual priority order | 346 | // TODO(ogniK): Have actual priority order |
| 347 | for (size_t i = 0; i < impl->shared_font_regions.size(); i++) { | 347 | for (std::size_t i = 0; i < impl->shared_font_regions.size(); i++) { |
| 348 | font_codes.push_back(static_cast<u32>(i)); | 348 | font_codes.push_back(static_cast<u32>(i)); |
| 349 | auto region = impl->GetSharedFontRegion(i); | 349 | auto region = impl->GetSharedFontRegion(i); |
| 350 | font_offsets.push_back(region.offset); | 350 | font_offsets.push_back(region.offset); |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index 25d5a93fa..d8b8037a8 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp | |||
| @@ -71,7 +71,7 @@ u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>& | |||
| 71 | } | 71 | } |
| 72 | 72 | ||
| 73 | u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) { | 73 | u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) { |
| 74 | size_t num_entries = input.size() / sizeof(IoctlRemapEntry); | 74 | std::size_t num_entries = input.size() / sizeof(IoctlRemapEntry); |
| 75 | 75 | ||
| 76 | LOG_WARNING(Service_NVDRV, "(STUBBED) called, num_entries=0x{:X}", num_entries); | 76 | LOG_WARNING(Service_NVDRV, "(STUBBED) called, num_entries=0x{:X}", num_entries); |
| 77 | 77 | ||
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 7455ddd19..d47b6f659 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp | |||
| @@ -23,7 +23,7 @@ | |||
| 23 | 23 | ||
| 24 | namespace Service::NVFlinger { | 24 | namespace Service::NVFlinger { |
| 25 | 25 | ||
| 26 | constexpr size_t SCREEN_REFRESH_RATE = 60; | 26 | constexpr std::size_t SCREEN_REFRESH_RATE = 60; |
| 27 | constexpr u64 frame_ticks = static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE); | 27 | constexpr u64 frame_ticks = static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE); |
| 28 | 28 | ||
| 29 | NVFlinger::NVFlinger() { | 29 | NVFlinger::NVFlinger() { |
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 9bb7c7b26..62f049660 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp | |||
| @@ -50,6 +50,7 @@ | |||
| 50 | #include "core/hle/service/nim/nim.h" | 50 | #include "core/hle/service/nim/nim.h" |
| 51 | #include "core/hle/service/ns/ns.h" | 51 | #include "core/hle/service/ns/ns.h" |
| 52 | #include "core/hle/service/nvdrv/nvdrv.h" | 52 | #include "core/hle/service/nvdrv/nvdrv.h" |
| 53 | #include "core/hle/service/nvflinger/nvflinger.h" | ||
| 53 | #include "core/hle/service/pcie/pcie.h" | 54 | #include "core/hle/service/pcie/pcie.h" |
| 54 | #include "core/hle/service/pctl/pctl.h" | 55 | #include "core/hle/service/pctl/pctl.h" |
| 55 | #include "core/hle/service/pcv/pcv.h" | 56 | #include "core/hle/service/pcv/pcv.h" |
| @@ -58,7 +59,6 @@ | |||
| 58 | #include "core/hle/service/psc/psc.h" | 59 | #include "core/hle/service/psc/psc.h" |
| 59 | #include "core/hle/service/service.h" | 60 | #include "core/hle/service/service.h" |
| 60 | #include "core/hle/service/set/settings.h" | 61 | #include "core/hle/service/set/settings.h" |
| 61 | #include "core/hle/service/sm/controller.h" | ||
| 62 | #include "core/hle/service/sm/sm.h" | 62 | #include "core/hle/service/sm/sm.h" |
| 63 | #include "core/hle/service/sockets/sockets.h" | 63 | #include "core/hle/service/sockets/sockets.h" |
| 64 | #include "core/hle/service/spl/module.h" | 64 | #include "core/hle/service/spl/module.h" |
| @@ -129,9 +129,9 @@ Kernel::SharedPtr<Kernel::ClientPort> ServiceFrameworkBase::CreatePort() { | |||
| 129 | return client_port; | 129 | return client_port; |
| 130 | } | 130 | } |
| 131 | 131 | ||
| 132 | void ServiceFrameworkBase::RegisterHandlersBase(const FunctionInfoBase* functions, size_t n) { | 132 | void ServiceFrameworkBase::RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n) { |
| 133 | handlers.reserve(handlers.size() + n); | 133 | handlers.reserve(handlers.size() + n); |
| 134 | for (size_t i = 0; i < n; ++i) { | 134 | for (std::size_t i = 0; i < n; ++i) { |
| 135 | // Usually this array is sorted by id already, so hint to insert at the end | 135 | // Usually this array is sorted by id already, so hint to insert at the end |
| 136 | handlers.emplace_hint(handlers.cend(), functions[i].expected_header, functions[i]); | 136 | handlers.emplace_hint(handlers.cend(), functions[i].expected_header, functions[i]); |
| 137 | } | 137 | } |
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 7a051523e..2fc57a82e 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h | |||
| @@ -88,7 +88,7 @@ private: | |||
| 88 | ServiceFrameworkBase(const char* service_name, u32 max_sessions, InvokerFn* handler_invoker); | 88 | ServiceFrameworkBase(const char* service_name, u32 max_sessions, InvokerFn* handler_invoker); |
| 89 | ~ServiceFrameworkBase(); | 89 | ~ServiceFrameworkBase(); |
| 90 | 90 | ||
| 91 | void RegisterHandlersBase(const FunctionInfoBase* functions, size_t n); | 91 | void RegisterHandlersBase(const FunctionInfoBase* functions, std::size_t n); |
| 92 | void ReportUnimplementedFunction(Kernel::HLERequestContext& ctx, const FunctionInfoBase* info); | 92 | void ReportUnimplementedFunction(Kernel::HLERequestContext& ctx, const FunctionInfoBase* info); |
| 93 | 93 | ||
| 94 | /// Identifier string used to connect to the service. | 94 | /// Identifier string used to connect to the service. |
| @@ -152,7 +152,7 @@ protected: | |||
| 152 | : ServiceFrameworkBase(service_name, max_sessions, Invoker) {} | 152 | : ServiceFrameworkBase(service_name, max_sessions, Invoker) {} |
| 153 | 153 | ||
| 154 | /// Registers handlers in the service. | 154 | /// Registers handlers in the service. |
| 155 | template <size_t N> | 155 | template <std::size_t N> |
| 156 | void RegisterHandlers(const FunctionInfo (&functions)[N]) { | 156 | void RegisterHandlers(const FunctionInfo (&functions)[N]) { |
| 157 | RegisterHandlers(functions, N); | 157 | RegisterHandlers(functions, N); |
| 158 | } | 158 | } |
| @@ -161,7 +161,7 @@ protected: | |||
| 161 | * Registers handlers in the service. Usually prefer using the other RegisterHandlers | 161 | * Registers handlers in the service. Usually prefer using the other RegisterHandlers |
| 162 | * overload in order to avoid needing to specify the array size. | 162 | * overload in order to avoid needing to specify the array size. |
| 163 | */ | 163 | */ |
| 164 | void RegisterHandlers(const FunctionInfo* functions, size_t n) { | 164 | void RegisterHandlers(const FunctionInfo* functions, std::size_t n) { |
| 165 | RegisterHandlersBase(functions, n); | 165 | RegisterHandlersBase(functions, n); |
| 166 | } | 166 | } |
| 167 | 167 | ||
diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp index 59eb20155..9e5af7839 100644 --- a/src/core/hle/service/set/set.cpp +++ b/src/core/hle/service/set/set.cpp | |||
| @@ -32,21 +32,21 @@ constexpr std::array<LanguageCode, 17> available_language_codes = {{ | |||
| 32 | LanguageCode::ZH_HANT, | 32 | LanguageCode::ZH_HANT, |
| 33 | }}; | 33 | }}; |
| 34 | 34 | ||
| 35 | constexpr size_t pre4_0_0_max_entries = 0xF; | 35 | constexpr std::size_t pre4_0_0_max_entries = 0xF; |
| 36 | constexpr size_t post4_0_0_max_entries = 0x40; | 36 | constexpr std::size_t post4_0_0_max_entries = 0x40; |
| 37 | 37 | ||
| 38 | LanguageCode GetLanguageCodeFromIndex(size_t index) { | 38 | LanguageCode GetLanguageCodeFromIndex(std::size_t index) { |
| 39 | return available_language_codes.at(index); | 39 | return available_language_codes.at(index); |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | template <size_t size> | 42 | template <std::size_t size> |
| 43 | static std::array<LanguageCode, size> MakeLanguageCodeSubset() { | 43 | static std::array<LanguageCode, size> MakeLanguageCodeSubset() { |
| 44 | std::array<LanguageCode, size> arr; | 44 | std::array<LanguageCode, size> arr; |
| 45 | std::copy_n(available_language_codes.begin(), size, arr.begin()); | 45 | std::copy_n(available_language_codes.begin(), size, arr.begin()); |
| 46 | return arr; | 46 | return arr; |
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | static void PushResponseLanguageCode(Kernel::HLERequestContext& ctx, size_t max_size) { | 49 | static void PushResponseLanguageCode(Kernel::HLERequestContext& ctx, std::size_t max_size) { |
| 50 | IPC::ResponseBuilder rb{ctx, 3}; | 50 | IPC::ResponseBuilder rb{ctx, 3}; |
| 51 | rb.Push(RESULT_SUCCESS); | 51 | rb.Push(RESULT_SUCCESS); |
| 52 | if (available_language_codes.size() > max_size) | 52 | if (available_language_codes.size() > max_size) |
diff --git a/src/core/hle/service/set/set.h b/src/core/hle/service/set/set.h index 5f0214359..266f13e46 100644 --- a/src/core/hle/service/set/set.h +++ b/src/core/hle/service/set/set.h | |||
| @@ -28,7 +28,7 @@ enum class LanguageCode : u64 { | |||
| 28 | ZH_HANS = 0x00736E61482D687A, | 28 | ZH_HANS = 0x00736E61482D687A, |
| 29 | ZH_HANT = 0x00746E61482D687A, | 29 | ZH_HANT = 0x00746E61482D687A, |
| 30 | }; | 30 | }; |
| 31 | LanguageCode GetLanguageCodeFromIndex(size_t idx); | 31 | LanguageCode GetLanguageCodeFromIndex(std::size_t idx); |
| 32 | 32 | ||
| 33 | class SET final : public ServiceFramework<SET> { | 33 | class SET final : public ServiceFramework<SET> { |
| 34 | public: | 34 | public: |
diff --git a/src/core/hle/service/sm/controller.cpp b/src/core/hle/service/sm/controller.cpp index cdf328a26..98f6e4111 100644 --- a/src/core/hle/service/sm/controller.cpp +++ b/src/core/hle/service/sm/controller.cpp | |||
| @@ -2,8 +2,11 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/assert.h" | ||
| 5 | #include "common/logging/log.h" | 6 | #include "common/logging/log.h" |
| 6 | #include "core/hle/ipc_helpers.h" | 7 | #include "core/hle/ipc_helpers.h" |
| 8 | #include "core/hle/kernel/client_session.h" | ||
| 9 | #include "core/hle/kernel/server_session.h" | ||
| 7 | #include "core/hle/kernel/session.h" | 10 | #include "core/hle/kernel/session.h" |
| 8 | #include "core/hle/service/sm/controller.h" | 11 | #include "core/hle/service/sm/controller.h" |
| 9 | 12 | ||
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index 18d1641b8..464e79d01 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp | |||
| @@ -15,6 +15,10 @@ | |||
| 15 | 15 | ||
| 16 | namespace Service::SM { | 16 | namespace Service::SM { |
| 17 | 17 | ||
| 18 | constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorModule::SM, 4); | ||
| 19 | constexpr ResultCode ERR_INVALID_NAME(ErrorModule::SM, 6); | ||
| 20 | constexpr ResultCode ERR_SERVICE_NOT_REGISTERED(ErrorModule::SM, 7); | ||
| 21 | |||
| 18 | ServiceManager::ServiceManager() = default; | 22 | ServiceManager::ServiceManager() = default; |
| 19 | ServiceManager::~ServiceManager() = default; | 23 | ServiceManager::~ServiceManager() = default; |
| 20 | 24 | ||
| @@ -24,10 +28,10 @@ void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) { | |||
| 24 | 28 | ||
| 25 | static ResultCode ValidateServiceName(const std::string& name) { | 29 | static ResultCode ValidateServiceName(const std::string& name) { |
| 26 | if (name.size() <= 0 || name.size() > 8) { | 30 | if (name.size() <= 0 || name.size() > 8) { |
| 27 | return ERR_INVALID_NAME_SIZE; | 31 | return ERR_INVALID_NAME; |
| 28 | } | 32 | } |
| 29 | if (name.find('\0') != std::string::npos) { | 33 | if (name.find('\0') != std::string::npos) { |
| 30 | return ERR_NAME_CONTAINS_NUL; | 34 | return ERR_INVALID_NAME; |
| 31 | } | 35 | } |
| 32 | return RESULT_SUCCESS; | 36 | return RESULT_SUCCESS; |
| 33 | } | 37 | } |
| @@ -104,7 +108,7 @@ void SM::GetService(Kernel::HLERequestContext& ctx) { | |||
| 104 | 108 | ||
| 105 | auto client_port = service_manager->GetServicePort(name); | 109 | auto client_port = service_manager->GetServicePort(name); |
| 106 | if (client_port.Failed()) { | 110 | if (client_port.Failed()) { |
| 107 | IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); | 111 | IPC::ResponseBuilder rb{ctx, 2}; |
| 108 | rb.Push(client_port.Code()); | 112 | rb.Push(client_port.Code()); |
| 109 | LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, client_port.Code().raw); | 113 | LOG_ERROR(Service_SM, "called service={} -> error 0x{:08X}", name, client_port.Code().raw); |
| 110 | if (name.length() == 0) | 114 | if (name.length() == 0) |
| @@ -117,8 +121,7 @@ void SM::GetService(Kernel::HLERequestContext& ctx) { | |||
| 117 | ASSERT(session.Succeeded()); | 121 | ASSERT(session.Succeeded()); |
| 118 | if (session.Succeeded()) { | 122 | if (session.Succeeded()) { |
| 119 | LOG_DEBUG(Service_SM, "called service={} -> session={}", name, (*session)->GetObjectId()); | 123 | LOG_DEBUG(Service_SM, "called service={} -> session={}", name, (*session)->GetObjectId()); |
| 120 | IPC::ResponseBuilder rb = | 124 | IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; |
| 121 | rp.MakeBuilder(2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles); | ||
| 122 | rb.Push(session.Code()); | 125 | rb.Push(session.Code()); |
| 123 | rb.PushMoveObjects(std::move(session).Unwrap()); | 126 | rb.PushMoveObjects(std::move(session).Unwrap()); |
| 124 | } | 127 | } |
diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h index a58d922a0..da2c51082 100644 --- a/src/core/hle/service/sm/sm.h +++ b/src/core/hle/service/sm/sm.h | |||
| @@ -36,12 +36,6 @@ private: | |||
| 36 | std::shared_ptr<ServiceManager> service_manager; | 36 | std::shared_ptr<ServiceManager> service_manager; |
| 37 | }; | 37 | }; |
| 38 | 38 | ||
| 39 | constexpr ResultCode ERR_SERVICE_NOT_REGISTERED(-1); | ||
| 40 | constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(-1); | ||
| 41 | constexpr ResultCode ERR_INVALID_NAME_SIZE(-1); | ||
| 42 | constexpr ResultCode ERR_NAME_CONTAINS_NUL(-1); | ||
| 43 | constexpr ResultCode ERR_ALREADY_REGISTERED(-1); | ||
| 44 | |||
| 45 | class ServiceManager { | 39 | class ServiceManager { |
| 46 | public: | 40 | public: |
| 47 | static void InstallInterfaces(std::shared_ptr<ServiceManager> self); | 41 | static void InstallInterfaces(std::shared_ptr<ServiceManager> self); |
diff --git a/src/core/hle/service/spl/module.cpp b/src/core/hle/service/spl/module.cpp index 0d8441fb1..44a6717d0 100644 --- a/src/core/hle/service/spl/module.cpp +++ b/src/core/hle/service/spl/module.cpp | |||
| @@ -21,7 +21,7 @@ Module::Interface::~Interface() = default; | |||
| 21 | void Module::Interface::GetRandomBytes(Kernel::HLERequestContext& ctx) { | 21 | void Module::Interface::GetRandomBytes(Kernel::HLERequestContext& ctx) { |
| 22 | IPC::RequestParser rp{ctx}; | 22 | IPC::RequestParser rp{ctx}; |
| 23 | 23 | ||
| 24 | size_t size = ctx.GetWriteBufferSize(); | 24 | std::size_t size = ctx.GetWriteBufferSize(); |
| 25 | 25 | ||
| 26 | std::vector<u8> data(size); | 26 | std::vector<u8> data(size); |
| 27 | std::generate(data.begin(), data.end(), std::rand); | 27 | std::generate(data.begin(), data.end(), std::rand); |
diff --git a/src/core/hle/service/ssl/ssl.cpp b/src/core/hle/service/ssl/ssl.cpp index 63b86e099..bc4f7a437 100644 --- a/src/core/hle/service/ssl/ssl.cpp +++ b/src/core/hle/service/ssl/ssl.cpp | |||
| @@ -71,7 +71,7 @@ private: | |||
| 71 | LOG_WARNING(Service_SSL, "(STUBBED) called"); | 71 | LOG_WARNING(Service_SSL, "(STUBBED) called"); |
| 72 | IPC::RequestParser rp{ctx}; | 72 | IPC::RequestParser rp{ctx}; |
| 73 | 73 | ||
| 74 | IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); | 74 | IPC::ResponseBuilder rb{ctx, 2}; |
| 75 | rb.Push(RESULT_SUCCESS); | 75 | rb.Push(RESULT_SUCCESS); |
| 76 | } | 76 | } |
| 77 | 77 | ||
| @@ -103,6 +103,7 @@ public: | |||
| 103 | } | 103 | } |
| 104 | 104 | ||
| 105 | private: | 105 | private: |
| 106 | u32 ssl_version{}; | ||
| 106 | void CreateContext(Kernel::HLERequestContext& ctx) { | 107 | void CreateContext(Kernel::HLERequestContext& ctx) { |
| 107 | LOG_WARNING(Service_SSL, "(STUBBED) called"); | 108 | LOG_WARNING(Service_SSL, "(STUBBED) called"); |
| 108 | 109 | ||
| @@ -112,10 +113,9 @@ private: | |||
| 112 | } | 113 | } |
| 113 | 114 | ||
| 114 | void SetInterfaceVersion(Kernel::HLERequestContext& ctx) { | 115 | void SetInterfaceVersion(Kernel::HLERequestContext& ctx) { |
| 115 | LOG_WARNING(Service_SSL, "(STUBBED) called"); | 116 | LOG_DEBUG(Service_SSL, "called"); |
| 116 | IPC::RequestParser rp{ctx}; | 117 | IPC::RequestParser rp{ctx}; |
| 117 | u32 unk1 = rp.Pop<u32>(); // Probably minor/major? | 118 | ssl_version = rp.Pop<u32>(); |
| 118 | u32 unk2 = rp.Pop<u32>(); // TODO(ogniK): Figure out what this does | ||
| 119 | 119 | ||
| 120 | IPC::ResponseBuilder rb{ctx, 2}; | 120 | IPC::ResponseBuilder rb{ctx, 2}; |
| 121 | rb.Push(RESULT_SUCCESS); | 121 | rb.Push(RESULT_SUCCESS); |
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 015b42cfd..bbc02abcc 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp | |||
| @@ -4,25 +4,28 @@ | |||
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <array> | 6 | #include <array> |
| 7 | #include <cstring> | ||
| 7 | #include <memory> | 8 | #include <memory> |
| 8 | #include <type_traits> | 9 | #include <type_traits> |
| 9 | #include <utility> | 10 | #include <utility> |
| 10 | #include <boost/optional.hpp> | 11 | #include <boost/optional.hpp> |
| 11 | #include "common/alignment.h" | 12 | #include "common/alignment.h" |
| 13 | #include "common/assert.h" | ||
| 14 | #include "common/common_funcs.h" | ||
| 15 | #include "common/logging/log.h" | ||
| 12 | #include "common/math_util.h" | 16 | #include "common/math_util.h" |
| 13 | #include "common/scope_exit.h" | 17 | #include "common/swap.h" |
| 14 | #include "core/core_timing.h" | 18 | #include "core/core_timing.h" |
| 15 | #include "core/hle/ipc_helpers.h" | 19 | #include "core/hle/ipc_helpers.h" |
| 16 | #include "core/hle/kernel/event.h" | 20 | #include "core/hle/kernel/event.h" |
| 17 | #include "core/hle/service/nvdrv/nvdrv.h" | 21 | #include "core/hle/service/nvdrv/nvdrv.h" |
| 18 | #include "core/hle/service/nvflinger/buffer_queue.h" | 22 | #include "core/hle/service/nvflinger/buffer_queue.h" |
| 23 | #include "core/hle/service/nvflinger/nvflinger.h" | ||
| 19 | #include "core/hle/service/vi/vi.h" | 24 | #include "core/hle/service/vi/vi.h" |
| 20 | #include "core/hle/service/vi/vi_m.h" | 25 | #include "core/hle/service/vi/vi_m.h" |
| 21 | #include "core/hle/service/vi/vi_s.h" | 26 | #include "core/hle/service/vi/vi_s.h" |
| 22 | #include "core/hle/service/vi/vi_u.h" | 27 | #include "core/hle/service/vi/vi_u.h" |
| 23 | #include "core/settings.h" | 28 | #include "core/settings.h" |
| 24 | #include "video_core/renderer_base.h" | ||
| 25 | #include "video_core/video_core.h" | ||
| 26 | 29 | ||
| 27 | namespace Service::VI { | 30 | namespace Service::VI { |
| 28 | 31 | ||
| @@ -38,7 +41,7 @@ static_assert(sizeof(DisplayInfo) == 0x60, "DisplayInfo has wrong size"); | |||
| 38 | class Parcel { | 41 | class Parcel { |
| 39 | public: | 42 | public: |
| 40 | // This default size was chosen arbitrarily. | 43 | // This default size was chosen arbitrarily. |
| 41 | static constexpr size_t DefaultBufferSize = 0x40; | 44 | static constexpr std::size_t DefaultBufferSize = 0x40; |
| 42 | Parcel() : buffer(DefaultBufferSize) {} | 45 | Parcel() : buffer(DefaultBufferSize) {} |
| 43 | explicit Parcel(std::vector<u8> data) : buffer(std::move(data)) {} | 46 | explicit Parcel(std::vector<u8> data) : buffer(std::move(data)) {} |
| 44 | virtual ~Parcel() = default; | 47 | virtual ~Parcel() = default; |
| @@ -66,7 +69,7 @@ public: | |||
| 66 | return val; | 69 | return val; |
| 67 | } | 70 | } |
| 68 | 71 | ||
| 69 | std::vector<u8> ReadBlock(size_t length) { | 72 | std::vector<u8> ReadBlock(std::size_t length) { |
| 70 | ASSERT(read_index + length <= buffer.size()); | 73 | ASSERT(read_index + length <= buffer.size()); |
| 71 | const u8* const begin = buffer.data() + read_index; | 74 | const u8* const begin = buffer.data() + read_index; |
| 72 | const u8* const end = begin + length; | 75 | const u8* const end = begin + length; |
| @@ -156,8 +159,8 @@ private: | |||
| 156 | static_assert(sizeof(Header) == 16, "ParcelHeader has wrong size"); | 159 | static_assert(sizeof(Header) == 16, "ParcelHeader has wrong size"); |
| 157 | 160 | ||
| 158 | std::vector<u8> buffer; | 161 | std::vector<u8> buffer; |
| 159 | size_t read_index = 0; | 162 | std::size_t read_index = 0; |
| 160 | size_t write_index = 0; | 163 | std::size_t write_index = 0; |
| 161 | }; | 164 | }; |
| 162 | 165 | ||
| 163 | class NativeWindow : public Parcel { | 166 | class NativeWindow : public Parcel { |
| @@ -647,7 +650,7 @@ private: | |||
| 647 | u64 layer_id = rp.Pop<u64>(); | 650 | u64 layer_id = rp.Pop<u64>(); |
| 648 | u64 z_value = rp.Pop<u64>(); | 651 | u64 z_value = rp.Pop<u64>(); |
| 649 | 652 | ||
| 650 | IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); | 653 | IPC::ResponseBuilder rb{ctx, 2}; |
| 651 | rb.Push(RESULT_SUCCESS); | 654 | rb.Push(RESULT_SUCCESS); |
| 652 | } | 655 | } |
| 653 | 656 | ||
| @@ -655,7 +658,7 @@ private: | |||
| 655 | IPC::RequestParser rp{ctx}; | 658 | IPC::RequestParser rp{ctx}; |
| 656 | u64 layer_id = rp.Pop<u64>(); | 659 | u64 layer_id = rp.Pop<u64>(); |
| 657 | bool visibility = rp.Pop<bool>(); | 660 | bool visibility = rp.Pop<bool>(); |
| 658 | IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); | 661 | IPC::ResponseBuilder rb{ctx, 2}; |
| 659 | rb.Push(RESULT_SUCCESS); | 662 | rb.Push(RESULT_SUCCESS); |
| 660 | LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x{:08X}, visibility={}", layer_id, | 663 | LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x{:08X}, visibility={}", layer_id, |
| 661 | visibility); | 664 | visibility); |
| @@ -762,7 +765,7 @@ private: | |||
| 762 | IPC::RequestParser rp{ctx}; | 765 | IPC::RequestParser rp{ctx}; |
| 763 | u64 display = rp.Pop<u64>(); | 766 | u64 display = rp.Pop<u64>(); |
| 764 | 767 | ||
| 765 | IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); | 768 | IPC::ResponseBuilder rb{ctx, 2}; |
| 766 | rb.Push(RESULT_SUCCESS); | 769 | rb.Push(RESULT_SUCCESS); |
| 767 | } | 770 | } |
| 768 | 771 | ||
| @@ -776,7 +779,7 @@ private: | |||
| 776 | 779 | ||
| 777 | u64 layer_id = nv_flinger->CreateLayer(display); | 780 | u64 layer_id = nv_flinger->CreateLayer(display); |
| 778 | 781 | ||
| 779 | IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0); | 782 | IPC::ResponseBuilder rb{ctx, 4}; |
| 780 | rb.Push(RESULT_SUCCESS); | 783 | rb.Push(RESULT_SUCCESS); |
| 781 | rb.Push(layer_id); | 784 | rb.Push(layer_id); |
| 782 | } | 785 | } |
| @@ -787,7 +790,7 @@ private: | |||
| 787 | u32 stack = rp.Pop<u32>(); | 790 | u32 stack = rp.Pop<u32>(); |
| 788 | u64 layer_id = rp.Pop<u64>(); | 791 | u64 layer_id = rp.Pop<u64>(); |
| 789 | 792 | ||
| 790 | IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); | 793 | IPC::ResponseBuilder rb{ctx, 2}; |
| 791 | rb.Push(RESULT_SUCCESS); | 794 | rb.Push(RESULT_SUCCESS); |
| 792 | } | 795 | } |
| 793 | 796 | ||
| @@ -795,7 +798,7 @@ private: | |||
| 795 | IPC::RequestParser rp{ctx}; | 798 | IPC::RequestParser rp{ctx}; |
| 796 | u64 layer_id = rp.Pop<u64>(); | 799 | u64 layer_id = rp.Pop<u64>(); |
| 797 | bool visibility = rp.Pop<bool>(); | 800 | bool visibility = rp.Pop<bool>(); |
| 798 | IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); | 801 | IPC::ResponseBuilder rb{ctx, 2}; |
| 799 | rb.Push(RESULT_SUCCESS); | 802 | rb.Push(RESULT_SUCCESS); |
| 800 | LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x{:X}, visibility={}", layer_id, | 803 | LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x{:X}, visibility={}", layer_id, |
| 801 | visibility); | 804 | visibility); |
| @@ -852,7 +855,7 @@ private: | |||
| 852 | 855 | ||
| 853 | ASSERT_MSG(name == "Default", "Non-default displays aren't supported yet"); | 856 | ASSERT_MSG(name == "Default", "Non-default displays aren't supported yet"); |
| 854 | 857 | ||
| 855 | IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0); | 858 | IPC::ResponseBuilder rb{ctx, 4}; |
| 856 | rb.Push(RESULT_SUCCESS); | 859 | rb.Push(RESULT_SUCCESS); |
| 857 | rb.Push<u64>(nv_flinger->OpenDisplay(name)); | 860 | rb.Push<u64>(nv_flinger->OpenDisplay(name)); |
| 858 | } | 861 | } |
| @@ -862,7 +865,7 @@ private: | |||
| 862 | IPC::RequestParser rp{ctx}; | 865 | IPC::RequestParser rp{ctx}; |
| 863 | u64 display_id = rp.Pop<u64>(); | 866 | u64 display_id = rp.Pop<u64>(); |
| 864 | 867 | ||
| 865 | IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); | 868 | IPC::ResponseBuilder rb{ctx, 2}; |
| 866 | rb.Push(RESULT_SUCCESS); | 869 | rb.Push(RESULT_SUCCESS); |
| 867 | } | 870 | } |
| 868 | 871 | ||
| @@ -871,7 +874,7 @@ private: | |||
| 871 | IPC::RequestParser rp{ctx}; | 874 | IPC::RequestParser rp{ctx}; |
| 872 | u64 display_id = rp.Pop<u64>(); | 875 | u64 display_id = rp.Pop<u64>(); |
| 873 | 876 | ||
| 874 | IPC::ResponseBuilder rb = rp.MakeBuilder(6, 0, 0); | 877 | IPC::ResponseBuilder rb{ctx, 6}; |
| 875 | rb.Push(RESULT_SUCCESS); | 878 | rb.Push(RESULT_SUCCESS); |
| 876 | 879 | ||
| 877 | if (Settings::values.use_docked_mode) { | 880 | if (Settings::values.use_docked_mode) { |
| @@ -889,7 +892,7 @@ private: | |||
| 889 | u32 scaling_mode = rp.Pop<u32>(); | 892 | u32 scaling_mode = rp.Pop<u32>(); |
| 890 | u64 unknown = rp.Pop<u64>(); | 893 | u64 unknown = rp.Pop<u64>(); |
| 891 | 894 | ||
| 892 | IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); | 895 | IPC::ResponseBuilder rb{ctx, 2}; |
| 893 | rb.Push(RESULT_SUCCESS); | 896 | rb.Push(RESULT_SUCCESS); |
| 894 | } | 897 | } |
| 895 | 898 | ||
| @@ -897,7 +900,7 @@ private: | |||
| 897 | IPC::RequestParser rp{ctx}; | 900 | IPC::RequestParser rp{ctx}; |
| 898 | DisplayInfo display_info; | 901 | DisplayInfo display_info; |
| 899 | ctx.WriteBuffer(&display_info, sizeof(DisplayInfo)); | 902 | ctx.WriteBuffer(&display_info, sizeof(DisplayInfo)); |
| 900 | IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0); | 903 | IPC::ResponseBuilder rb{ctx, 4}; |
| 901 | rb.Push(RESULT_SUCCESS); | 904 | rb.Push(RESULT_SUCCESS); |
| 902 | rb.Push<u64>(1); | 905 | rb.Push<u64>(1); |
| 903 | LOG_WARNING(Service_VI, "(STUBBED) called"); | 906 | LOG_WARNING(Service_VI, "(STUBBED) called"); |
| @@ -918,7 +921,7 @@ private: | |||
| 918 | u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id); | 921 | u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id); |
| 919 | 922 | ||
| 920 | NativeWindow native_window{buffer_queue_id}; | 923 | NativeWindow native_window{buffer_queue_id}; |
| 921 | IPC::ResponseBuilder rb = rp.MakeBuilder(4, 0, 0); | 924 | IPC::ResponseBuilder rb{ctx, 4}; |
| 922 | rb.Push(RESULT_SUCCESS); | 925 | rb.Push(RESULT_SUCCESS); |
| 923 | rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize())); | 926 | rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize())); |
| 924 | } | 927 | } |
| @@ -937,7 +940,7 @@ private: | |||
| 937 | u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id); | 940 | u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id); |
| 938 | 941 | ||
| 939 | NativeWindow native_window{buffer_queue_id}; | 942 | NativeWindow native_window{buffer_queue_id}; |
| 940 | IPC::ResponseBuilder rb = rp.MakeBuilder(6, 0, 0); | 943 | IPC::ResponseBuilder rb{ctx, 6}; |
| 941 | rb.Push(RESULT_SUCCESS); | 944 | rb.Push(RESULT_SUCCESS); |
| 942 | rb.Push(layer_id); | 945 | rb.Push(layer_id); |
| 943 | rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize())); | 946 | rb.Push<u64>(ctx.WriteBuffer(native_window.Serialize())); |
| @@ -949,7 +952,7 @@ private: | |||
| 949 | IPC::RequestParser rp{ctx}; | 952 | IPC::RequestParser rp{ctx}; |
| 950 | u64 layer_id = rp.Pop<u64>(); | 953 | u64 layer_id = rp.Pop<u64>(); |
| 951 | 954 | ||
| 952 | IPC::ResponseBuilder rb = rp.MakeBuilder(2, 0, 0); | 955 | IPC::ResponseBuilder rb{ctx, 2}; |
| 953 | rb.Push(RESULT_SUCCESS); | 956 | rb.Push(RESULT_SUCCESS); |
| 954 | } | 957 | } |
| 955 | 958 | ||
| @@ -960,7 +963,7 @@ private: | |||
| 960 | 963 | ||
| 961 | auto vsync_event = nv_flinger->GetVsyncEvent(display_id); | 964 | auto vsync_event = nv_flinger->GetVsyncEvent(display_id); |
| 962 | 965 | ||
| 963 | IPC::ResponseBuilder rb = rp.MakeBuilder(2, 1, 0); | 966 | IPC::ResponseBuilder rb{ctx, 2, 1}; |
| 964 | rb.Push(RESULT_SUCCESS); | 967 | rb.Push(RESULT_SUCCESS); |
| 965 | rb.PushCopyObjects(vsync_event); | 968 | rb.PushCopyObjects(vsync_event); |
| 966 | } | 969 | } |
diff --git a/src/core/hle/service/vi/vi.h b/src/core/hle/service/vi/vi.h index c2dc83605..e3963502a 100644 --- a/src/core/hle/service/vi/vi.h +++ b/src/core/hle/service/vi/vi.h | |||
| @@ -4,11 +4,10 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "core/hle/service/nvflinger/nvflinger.h" | ||
| 8 | #include "core/hle/service/service.h" | 7 | #include "core/hle/service/service.h" |
| 9 | 8 | ||
| 10 | namespace CoreTiming { | 9 | namespace Service::NVFlinger { |
| 11 | struct EventType; | 10 | class NVFlinger; |
| 12 | } | 11 | } |
| 13 | 12 | ||
| 14 | namespace Service::VI { | 13 | namespace Service::VI { |
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index 2b8f78136..c1824b9c3 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp | |||
| @@ -14,11 +14,9 @@ | |||
| 14 | #include "core/gdbstub/gdbstub.h" | 14 | #include "core/gdbstub/gdbstub.h" |
| 15 | #include "core/hle/kernel/kernel.h" | 15 | #include "core/hle/kernel/kernel.h" |
| 16 | #include "core/hle/kernel/process.h" | 16 | #include "core/hle/kernel/process.h" |
| 17 | #include "core/hle/kernel/resource_limit.h" | ||
| 18 | #include "core/hle/service/filesystem/filesystem.h" | 17 | #include "core/hle/service/filesystem/filesystem.h" |
| 19 | #include "core/loader/deconstructed_rom_directory.h" | 18 | #include "core/loader/deconstructed_rom_directory.h" |
| 20 | #include "core/loader/nso.h" | 19 | #include "core/loader/nso.h" |
| 21 | #include "core/memory.h" | ||
| 22 | 20 | ||
| 23 | namespace Loader { | 21 | namespace Loader { |
| 24 | 22 | ||
| @@ -88,8 +86,7 @@ FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::Virtua | |||
| 88 | return FileType::Error; | 86 | return FileType::Error; |
| 89 | } | 87 | } |
| 90 | 88 | ||
| 91 | ResultStatus AppLoader_DeconstructedRomDirectory::Load( | 89 | ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process) { |
| 92 | Kernel::SharedPtr<Kernel::Process>& process) { | ||
| 93 | if (is_loaded) { | 90 | if (is_loaded) { |
| 94 | return ResultStatus::ErrorAlreadyLoaded; | 91 | return ResultStatus::ErrorAlreadyLoaded; |
| 95 | } | 92 | } |
| @@ -127,12 +124,16 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load( | |||
| 127 | metadata.Print(); | 124 | metadata.Print(); |
| 128 | 125 | ||
| 129 | const FileSys::ProgramAddressSpaceType arch_bits{metadata.GetAddressSpaceType()}; | 126 | const FileSys::ProgramAddressSpaceType arch_bits{metadata.GetAddressSpaceType()}; |
| 130 | if (arch_bits == FileSys::ProgramAddressSpaceType::Is32Bit) { | 127 | if (arch_bits == FileSys::ProgramAddressSpaceType::Is32Bit || |
| 128 | arch_bits == FileSys::ProgramAddressSpaceType::Is32BitNoMap) { | ||
| 131 | return ResultStatus::Error32BitISA; | 129 | return ResultStatus::Error32BitISA; |
| 132 | } | 130 | } |
| 133 | 131 | ||
| 132 | process.LoadFromMetadata(metadata); | ||
| 133 | |||
| 134 | // Load NSO modules | 134 | // Load NSO modules |
| 135 | VAddr next_load_addr{Memory::PROCESS_IMAGE_VADDR}; | 135 | const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); |
| 136 | VAddr next_load_addr = base_address; | ||
| 136 | for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", | 137 | for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", |
| 137 | "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) { | 138 | "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) { |
| 138 | const FileSys::VirtualFile module_file = dir->GetFile(module); | 139 | const FileSys::VirtualFile module_file = dir->GetFile(module); |
| @@ -145,13 +146,7 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load( | |||
| 145 | } | 146 | } |
| 146 | } | 147 | } |
| 147 | 148 | ||
| 148 | auto& kernel = Core::System::GetInstance().Kernel(); | 149 | process.Run(base_address, metadata.GetMainThreadPriority(), metadata.GetMainThreadStackSize()); |
| 149 | process->program_id = metadata.GetTitleID(); | ||
| 150 | process->svc_access_mask.set(); | ||
| 151 | process->resource_limit = | ||
| 152 | kernel.ResourceLimitForCategory(Kernel::ResourceLimitCategory::APPLICATION); | ||
| 153 | process->Run(Memory::PROCESS_IMAGE_VADDR, metadata.GetMainThreadPriority(), | ||
| 154 | metadata.GetMainThreadStackSize()); | ||
| 155 | 150 | ||
| 156 | // Find the RomFS by searching for a ".romfs" file in this directory | 151 | // Find the RomFS by searching for a ".romfs" file in this directory |
| 157 | const auto& files = dir->GetFiles(); | 152 | const auto& files = dir->GetFiles(); |
diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h index 8a0dc1b1e..d109ed2b5 100644 --- a/src/core/loader/deconstructed_rom_directory.h +++ b/src/core/loader/deconstructed_rom_directory.h | |||
| @@ -7,7 +7,6 @@ | |||
| 7 | #include <string> | 7 | #include <string> |
| 8 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | #include "core/file_sys/program_metadata.h" | 9 | #include "core/file_sys/program_metadata.h" |
| 10 | #include "core/hle/kernel/object.h" | ||
| 11 | #include "core/loader/loader.h" | 10 | #include "core/loader/loader.h" |
| 12 | 11 | ||
| 13 | namespace Loader { | 12 | namespace Loader { |
| @@ -38,7 +37,7 @@ public: | |||
| 38 | return IdentifyType(file); | 37 | return IdentifyType(file); |
| 39 | } | 38 | } |
| 40 | 39 | ||
| 41 | ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; | 40 | ResultStatus Load(Kernel::Process& process) override; |
| 42 | 41 | ||
| 43 | ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; | 42 | ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; |
| 44 | ResultStatus ReadIcon(std::vector<u8>& buffer) override; | 43 | ResultStatus ReadIcon(std::vector<u8>& buffer) override; |
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index 120e1e133..e67b49fc9 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp | |||
| @@ -12,7 +12,7 @@ | |||
| 12 | #include "core/core.h" | 12 | #include "core/core.h" |
| 13 | #include "core/hle/kernel/kernel.h" | 13 | #include "core/hle/kernel/kernel.h" |
| 14 | #include "core/hle/kernel/process.h" | 14 | #include "core/hle/kernel/process.h" |
| 15 | #include "core/hle/kernel/resource_limit.h" | 15 | #include "core/hle/kernel/vm_manager.h" |
| 16 | #include "core/loader/elf.h" | 16 | #include "core/loader/elf.h" |
| 17 | #include "core/memory.h" | 17 | #include "core/memory.h" |
| 18 | 18 | ||
| @@ -189,7 +189,7 @@ private: | |||
| 189 | 189 | ||
| 190 | u32* sectionAddrs; | 190 | u32* sectionAddrs; |
| 191 | bool relocate; | 191 | bool relocate; |
| 192 | u32 entryPoint; | 192 | VAddr entryPoint; |
| 193 | 193 | ||
| 194 | public: | 194 | public: |
| 195 | explicit ElfReader(void* ptr); | 195 | explicit ElfReader(void* ptr); |
| @@ -205,13 +205,13 @@ public: | |||
| 205 | ElfMachine GetMachine() const { | 205 | ElfMachine GetMachine() const { |
| 206 | return (ElfMachine)(header->e_machine); | 206 | return (ElfMachine)(header->e_machine); |
| 207 | } | 207 | } |
| 208 | u32 GetEntryPoint() const { | 208 | VAddr GetEntryPoint() const { |
| 209 | return entryPoint; | 209 | return entryPoint; |
| 210 | } | 210 | } |
| 211 | u32 GetFlags() const { | 211 | u32 GetFlags() const { |
| 212 | return (u32)(header->e_flags); | 212 | return (u32)(header->e_flags); |
| 213 | } | 213 | } |
| 214 | SharedPtr<CodeSet> LoadInto(u32 vaddr); | 214 | SharedPtr<CodeSet> LoadInto(VAddr vaddr); |
| 215 | 215 | ||
| 216 | int GetNumSegments() const { | 216 | int GetNumSegments() const { |
| 217 | return (int)(header->e_phnum); | 217 | return (int)(header->e_phnum); |
| @@ -274,7 +274,7 @@ const char* ElfReader::GetSectionName(int section) const { | |||
| 274 | return nullptr; | 274 | return nullptr; |
| 275 | } | 275 | } |
| 276 | 276 | ||
| 277 | SharedPtr<CodeSet> ElfReader::LoadInto(u32 vaddr) { | 277 | SharedPtr<CodeSet> ElfReader::LoadInto(VAddr vaddr) { |
| 278 | LOG_DEBUG(Loader, "String section: {}", header->e_shstrndx); | 278 | LOG_DEBUG(Loader, "String section: {}", header->e_shstrndx); |
| 279 | 279 | ||
| 280 | // Should we relocate? | 280 | // Should we relocate? |
| @@ -289,24 +289,24 @@ SharedPtr<CodeSet> ElfReader::LoadInto(u32 vaddr) { | |||
| 289 | LOG_DEBUG(Loader, "{} segments:", header->e_phnum); | 289 | LOG_DEBUG(Loader, "{} segments:", header->e_phnum); |
| 290 | 290 | ||
| 291 | // First pass : Get the bits into RAM | 291 | // First pass : Get the bits into RAM |
| 292 | u32 base_addr = relocate ? vaddr : 0; | 292 | const VAddr base_addr = relocate ? vaddr : 0; |
| 293 | 293 | ||
| 294 | u32 total_image_size = 0; | 294 | u64 total_image_size = 0; |
| 295 | for (unsigned int i = 0; i < header->e_phnum; ++i) { | 295 | for (unsigned int i = 0; i < header->e_phnum; ++i) { |
| 296 | Elf32_Phdr* p = &segments[i]; | 296 | const Elf32_Phdr* p = &segments[i]; |
| 297 | if (p->p_type == PT_LOAD) { | 297 | if (p->p_type == PT_LOAD) { |
| 298 | total_image_size += (p->p_memsz + 0xFFF) & ~0xFFF; | 298 | total_image_size += (p->p_memsz + 0xFFF) & ~0xFFF; |
| 299 | } | 299 | } |
| 300 | } | 300 | } |
| 301 | 301 | ||
| 302 | std::vector<u8> program_image(total_image_size); | 302 | std::vector<u8> program_image(total_image_size); |
| 303 | size_t current_image_position = 0; | 303 | std::size_t current_image_position = 0; |
| 304 | 304 | ||
| 305 | auto& kernel = Core::System::GetInstance().Kernel(); | 305 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 306 | SharedPtr<CodeSet> codeset = CodeSet::Create(kernel, ""); | 306 | SharedPtr<CodeSet> codeset = CodeSet::Create(kernel, ""); |
| 307 | 307 | ||
| 308 | for (unsigned int i = 0; i < header->e_phnum; ++i) { | 308 | for (unsigned int i = 0; i < header->e_phnum; ++i) { |
| 309 | Elf32_Phdr* p = &segments[i]; | 309 | const Elf32_Phdr* p = &segments[i]; |
| 310 | LOG_DEBUG(Loader, "Type: {} Vaddr: {:08X} Filesz: {:08X} Memsz: {:08X} ", p->p_type, | 310 | LOG_DEBUG(Loader, "Type: {} Vaddr: {:08X} Filesz: {:08X} Memsz: {:08X} ", p->p_type, |
| 311 | p->p_vaddr, p->p_filesz, p->p_memsz); | 311 | p->p_vaddr, p->p_filesz, p->p_memsz); |
| 312 | 312 | ||
| @@ -333,8 +333,8 @@ SharedPtr<CodeSet> ElfReader::LoadInto(u32 vaddr) { | |||
| 333 | continue; | 333 | continue; |
| 334 | } | 334 | } |
| 335 | 335 | ||
| 336 | u32 segment_addr = base_addr + p->p_vaddr; | 336 | const VAddr segment_addr = base_addr + p->p_vaddr; |
| 337 | u32 aligned_size = (p->p_memsz + 0xFFF) & ~0xFFF; | 337 | const u32 aligned_size = (p->p_memsz + 0xFFF) & ~0xFFF; |
| 338 | 338 | ||
| 339 | codeset_segment->offset = current_image_position; | 339 | codeset_segment->offset = current_image_position; |
| 340 | codeset_segment->addr = segment_addr; | 340 | codeset_segment->addr = segment_addr; |
| @@ -387,7 +387,7 @@ FileType AppLoader_ELF::IdentifyType(const FileSys::VirtualFile& file) { | |||
| 387 | return FileType::Error; | 387 | return FileType::Error; |
| 388 | } | 388 | } |
| 389 | 389 | ||
| 390 | ResultStatus AppLoader_ELF::Load(Kernel::SharedPtr<Kernel::Process>& process) { | 390 | ResultStatus AppLoader_ELF::Load(Kernel::Process& process) { |
| 391 | if (is_loaded) | 391 | if (is_loaded) |
| 392 | return ResultStatus::ErrorAlreadyLoaded; | 392 | return ResultStatus::ErrorAlreadyLoaded; |
| 393 | 393 | ||
| @@ -395,19 +395,13 @@ ResultStatus AppLoader_ELF::Load(Kernel::SharedPtr<Kernel::Process>& process) { | |||
| 395 | if (buffer.size() != file->GetSize()) | 395 | if (buffer.size() != file->GetSize()) |
| 396 | return ResultStatus::ErrorIncorrectELFFileSize; | 396 | return ResultStatus::ErrorIncorrectELFFileSize; |
| 397 | 397 | ||
| 398 | const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); | ||
| 398 | ElfReader elf_reader(&buffer[0]); | 399 | ElfReader elf_reader(&buffer[0]); |
| 399 | SharedPtr<CodeSet> codeset = elf_reader.LoadInto(Memory::PROCESS_IMAGE_VADDR); | 400 | SharedPtr<CodeSet> codeset = elf_reader.LoadInto(base_address); |
| 400 | codeset->name = file->GetName(); | 401 | codeset->name = file->GetName(); |
| 401 | 402 | ||
| 402 | process->LoadModule(codeset, codeset->entrypoint); | 403 | process.LoadModule(codeset, codeset->entrypoint); |
| 403 | process->svc_access_mask.set(); | 404 | process.Run(codeset->entrypoint, 48, Memory::DEFAULT_STACK_SIZE); |
| 404 | |||
| 405 | // Attach the default resource limit (APPLICATION) to the process | ||
| 406 | auto& kernel = Core::System::GetInstance().Kernel(); | ||
| 407 | process->resource_limit = | ||
| 408 | kernel.ResourceLimitForCategory(Kernel::ResourceLimitCategory::APPLICATION); | ||
| 409 | |||
| 410 | process->Run(codeset->entrypoint, 48, Memory::DEFAULT_STACK_SIZE); | ||
| 411 | 405 | ||
| 412 | is_loaded = true; | 406 | is_loaded = true; |
| 413 | return ResultStatus::Success; | 407 | return ResultStatus::Success; |
diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h index b8fb982d0..6af76441c 100644 --- a/src/core/loader/elf.h +++ b/src/core/loader/elf.h | |||
| @@ -8,9 +8,6 @@ | |||
| 8 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | #include "core/loader/loader.h" | 9 | #include "core/loader/loader.h" |
| 10 | 10 | ||
| 11 | //////////////////////////////////////////////////////////////////////////////////////////////////// | ||
| 12 | // Loader namespace | ||
| 13 | |||
| 14 | namespace Loader { | 11 | namespace Loader { |
| 15 | 12 | ||
| 16 | /// Loads an ELF/AXF file | 13 | /// Loads an ELF/AXF file |
| @@ -29,7 +26,7 @@ public: | |||
| 29 | return IdentifyType(file); | 26 | return IdentifyType(file); |
| 30 | } | 27 | } |
| 31 | 28 | ||
| 32 | ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; | 29 | ResultStatus Load(Kernel::Process& process) override; |
| 33 | }; | 30 | }; |
| 34 | 31 | ||
| 35 | } // namespace Loader | 32 | } // namespace Loader |
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index fa43a2650..f2a183ba1 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp | |||
| @@ -155,7 +155,7 @@ constexpr std::array<const char*, 58> RESULT_MESSAGES{ | |||
| 155 | }; | 155 | }; |
| 156 | 156 | ||
| 157 | std::ostream& operator<<(std::ostream& os, ResultStatus status) { | 157 | std::ostream& operator<<(std::ostream& os, ResultStatus status) { |
| 158 | os << RESULT_MESSAGES.at(static_cast<size_t>(status)); | 158 | os << RESULT_MESSAGES.at(static_cast<std::size_t>(status)); |
| 159 | return os; | 159 | return os; |
| 160 | } | 160 | } |
| 161 | 161 | ||
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 843c4bb91..20e66109b 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h | |||
| @@ -12,7 +12,6 @@ | |||
| 12 | #include <boost/optional.hpp> | 12 | #include <boost/optional.hpp> |
| 13 | #include "common/common_types.h" | 13 | #include "common/common_types.h" |
| 14 | #include "core/file_sys/vfs.h" | 14 | #include "core/file_sys/vfs.h" |
| 15 | #include "core/hle/kernel/object.h" | ||
| 16 | 15 | ||
| 17 | namespace Kernel { | 16 | namespace Kernel { |
| 18 | struct AddressMapping; | 17 | struct AddressMapping; |
| @@ -136,7 +135,7 @@ public: | |||
| 136 | * @param process The newly created process. | 135 | * @param process The newly created process. |
| 137 | * @return The status result of the operation. | 136 | * @return The status result of the operation. |
| 138 | */ | 137 | */ |
| 139 | virtual ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) = 0; | 138 | virtual ResultStatus Load(Kernel::Process& process) = 0; |
| 140 | 139 | ||
| 141 | /** | 140 | /** |
| 142 | * Loads the system mode that this application needs. | 141 | * Loads the system mode that this application needs. |
diff --git a/src/core/loader/nax.cpp b/src/core/loader/nax.cpp index b46d81c02..073fb9d2f 100644 --- a/src/core/loader/nax.cpp +++ b/src/core/loader/nax.cpp | |||
| @@ -11,6 +11,20 @@ | |||
| 11 | #include "core/loader/nca.h" | 11 | #include "core/loader/nca.h" |
| 12 | 12 | ||
| 13 | namespace Loader { | 13 | namespace Loader { |
| 14 | namespace { | ||
| 15 | FileType IdentifyTypeImpl(const FileSys::NAX& nax) { | ||
| 16 | if (nax.GetStatus() != ResultStatus::Success) { | ||
| 17 | return FileType::Error; | ||
| 18 | } | ||
| 19 | |||
| 20 | const auto nca = nax.AsNCA(); | ||
| 21 | if (nca == nullptr || nca->GetStatus() != ResultStatus::Success) { | ||
| 22 | return FileType::Error; | ||
| 23 | } | ||
| 24 | |||
| 25 | return FileType::NAX; | ||
| 26 | } | ||
| 27 | } // Anonymous namespace | ||
| 14 | 28 | ||
| 15 | AppLoader_NAX::AppLoader_NAX(FileSys::VirtualFile file) | 29 | AppLoader_NAX::AppLoader_NAX(FileSys::VirtualFile file) |
| 16 | : AppLoader(file), nax(std::make_unique<FileSys::NAX>(file)), | 30 | : AppLoader(file), nax(std::make_unique<FileSys::NAX>(file)), |
| @@ -19,17 +33,15 @@ AppLoader_NAX::AppLoader_NAX(FileSys::VirtualFile file) | |||
| 19 | AppLoader_NAX::~AppLoader_NAX() = default; | 33 | AppLoader_NAX::~AppLoader_NAX() = default; |
| 20 | 34 | ||
| 21 | FileType AppLoader_NAX::IdentifyType(const FileSys::VirtualFile& file) { | 35 | FileType AppLoader_NAX::IdentifyType(const FileSys::VirtualFile& file) { |
| 22 | FileSys::NAX nax(file); | 36 | const FileSys::NAX nax(file); |
| 23 | 37 | return IdentifyTypeImpl(nax); | |
| 24 | if (nax.GetStatus() == ResultStatus::Success && nax.AsNCA() != nullptr && | 38 | } |
| 25 | nax.AsNCA()->GetStatus() == ResultStatus::Success) { | ||
| 26 | return FileType::NAX; | ||
| 27 | } | ||
| 28 | 39 | ||
| 29 | return FileType::Error; | 40 | FileType AppLoader_NAX::GetFileType() { |
| 41 | return IdentifyTypeImpl(*nax); | ||
| 30 | } | 42 | } |
| 31 | 43 | ||
| 32 | ResultStatus AppLoader_NAX::Load(Kernel::SharedPtr<Kernel::Process>& process) { | 44 | ResultStatus AppLoader_NAX::Load(Kernel::Process& process) { |
| 33 | if (is_loaded) { | 45 | if (is_loaded) { |
| 34 | return ResultStatus::ErrorAlreadyLoaded; | 46 | return ResultStatus::ErrorAlreadyLoaded; |
| 35 | } | 47 | } |
diff --git a/src/core/loader/nax.h b/src/core/loader/nax.h index 4dbae2918..fc3c01876 100644 --- a/src/core/loader/nax.h +++ b/src/core/loader/nax.h | |||
| @@ -31,11 +31,9 @@ public: | |||
| 31 | */ | 31 | */ |
| 32 | static FileType IdentifyType(const FileSys::VirtualFile& file); | 32 | static FileType IdentifyType(const FileSys::VirtualFile& file); |
| 33 | 33 | ||
| 34 | FileType GetFileType() override { | 34 | FileType GetFileType() override; |
| 35 | return IdentifyType(file); | ||
| 36 | } | ||
| 37 | 35 | ||
| 38 | ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; | 36 | ResultStatus Load(Kernel::Process& process) override; |
| 39 | 37 | ||
| 40 | ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; | 38 | ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; |
| 41 | ResultStatus ReadProgramId(u64& out_program_id) override; | 39 | ResultStatus ReadProgramId(u64& out_program_id) override; |
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp index 6aaffae59..7e1b0d84f 100644 --- a/src/core/loader/nca.cpp +++ b/src/core/loader/nca.cpp | |||
| @@ -30,7 +30,7 @@ FileType AppLoader_NCA::IdentifyType(const FileSys::VirtualFile& file) { | |||
| 30 | return FileType::Error; | 30 | return FileType::Error; |
| 31 | } | 31 | } |
| 32 | 32 | ||
| 33 | ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) { | 33 | ResultStatus AppLoader_NCA::Load(Kernel::Process& process) { |
| 34 | if (is_loaded) { | 34 | if (is_loaded) { |
| 35 | return ResultStatus::ErrorAlreadyLoaded; | 35 | return ResultStatus::ErrorAlreadyLoaded; |
| 36 | } | 36 | } |
diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h index 10be197c4..95d9b73a1 100644 --- a/src/core/loader/nca.h +++ b/src/core/loader/nca.h | |||
| @@ -6,7 +6,6 @@ | |||
| 6 | 6 | ||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "core/file_sys/vfs.h" | 8 | #include "core/file_sys/vfs.h" |
| 9 | #include "core/hle/kernel/object.h" | ||
| 10 | #include "core/loader/loader.h" | 9 | #include "core/loader/loader.h" |
| 11 | 10 | ||
| 12 | namespace FileSys { | 11 | namespace FileSys { |
| @@ -34,7 +33,7 @@ public: | |||
| 34 | return IdentifyType(file); | 33 | return IdentifyType(file); |
| 35 | } | 34 | } |
| 36 | 35 | ||
| 37 | ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; | 36 | ResultStatus Load(Kernel::Process& process) override; |
| 38 | 37 | ||
| 39 | ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; | 38 | ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; |
| 40 | u64 ReadRomFSIVFCOffset() const override; | 39 | u64 ReadRomFSIVFCOffset() const override; |
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index c49ec34ab..c10f826a4 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp | |||
| @@ -16,7 +16,7 @@ | |||
| 16 | #include "core/gdbstub/gdbstub.h" | 16 | #include "core/gdbstub/gdbstub.h" |
| 17 | #include "core/hle/kernel/kernel.h" | 17 | #include "core/hle/kernel/kernel.h" |
| 18 | #include "core/hle/kernel/process.h" | 18 | #include "core/hle/kernel/process.h" |
| 19 | #include "core/hle/kernel/resource_limit.h" | 19 | #include "core/hle/kernel/vm_manager.h" |
| 20 | #include "core/loader/nro.h" | 20 | #include "core/loader/nro.h" |
| 21 | #include "core/memory.h" | 21 | #include "core/memory.h" |
| 22 | 22 | ||
| @@ -175,23 +175,19 @@ bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) { | |||
| 175 | return true; | 175 | return true; |
| 176 | } | 176 | } |
| 177 | 177 | ||
| 178 | ResultStatus AppLoader_NRO::Load(Kernel::SharedPtr<Kernel::Process>& process) { | 178 | ResultStatus AppLoader_NRO::Load(Kernel::Process& process) { |
| 179 | if (is_loaded) { | 179 | if (is_loaded) { |
| 180 | return ResultStatus::ErrorAlreadyLoaded; | 180 | return ResultStatus::ErrorAlreadyLoaded; |
| 181 | } | 181 | } |
| 182 | 182 | ||
| 183 | // Load NRO | 183 | // Load NRO |
| 184 | static constexpr VAddr base_addr{Memory::PROCESS_IMAGE_VADDR}; | 184 | const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); |
| 185 | 185 | ||
| 186 | if (!LoadNro(file, base_addr)) { | 186 | if (!LoadNro(file, base_address)) { |
| 187 | return ResultStatus::ErrorLoadingNRO; | 187 | return ResultStatus::ErrorLoadingNRO; |
| 188 | } | 188 | } |
| 189 | 189 | ||
| 190 | auto& kernel = Core::System::GetInstance().Kernel(); | 190 | process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE); |
| 191 | process->svc_access_mask.set(); | ||
| 192 | process->resource_limit = | ||
| 193 | kernel.ResourceLimitForCategory(Kernel::ResourceLimitCategory::APPLICATION); | ||
| 194 | process->Run(base_addr, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE); | ||
| 195 | 191 | ||
| 196 | is_loaded = true; | 192 | is_loaded = true; |
| 197 | return ResultStatus::Success; | 193 | return ResultStatus::Success; |
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h index 96d2de305..04b46119a 100644 --- a/src/core/loader/nro.h +++ b/src/core/loader/nro.h | |||
| @@ -6,7 +6,6 @@ | |||
| 6 | 6 | ||
| 7 | #include <string> | 7 | #include <string> |
| 8 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | #include "core/hle/kernel/object.h" | ||
| 10 | #include "core/loader/linker.h" | 9 | #include "core/loader/linker.h" |
| 11 | #include "core/loader/loader.h" | 10 | #include "core/loader/loader.h" |
| 12 | 11 | ||
| @@ -33,7 +32,7 @@ public: | |||
| 33 | return IdentifyType(file); | 32 | return IdentifyType(file); |
| 34 | } | 33 | } |
| 35 | 34 | ||
| 36 | ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; | 35 | ResultStatus Load(Kernel::Process& process) override; |
| 37 | 36 | ||
| 38 | ResultStatus ReadIcon(std::vector<u8>& buffer) override; | 37 | ResultStatus ReadIcon(std::vector<u8>& buffer) override; |
| 39 | ResultStatus ReadProgramId(u64& out_program_id) override; | 38 | ResultStatus ReadProgramId(u64& out_program_id) override; |
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 3c6306818..cbe2a3e53 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp | |||
| @@ -13,7 +13,7 @@ | |||
| 13 | #include "core/gdbstub/gdbstub.h" | 13 | #include "core/gdbstub/gdbstub.h" |
| 14 | #include "core/hle/kernel/kernel.h" | 14 | #include "core/hle/kernel/kernel.h" |
| 15 | #include "core/hle/kernel/process.h" | 15 | #include "core/hle/kernel/process.h" |
| 16 | #include "core/hle/kernel/resource_limit.h" | 16 | #include "core/hle/kernel/vm_manager.h" |
| 17 | #include "core/loader/nso.h" | 17 | #include "core/loader/nso.h" |
| 18 | #include "core/memory.h" | 18 | #include "core/memory.h" |
| 19 | 19 | ||
| @@ -32,11 +32,18 @@ static_assert(sizeof(NsoSegmentHeader) == 0x10, "NsoSegmentHeader has incorrect | |||
| 32 | 32 | ||
| 33 | struct NsoHeader { | 33 | struct NsoHeader { |
| 34 | u32_le magic; | 34 | u32_le magic; |
| 35 | INSERT_PADDING_BYTES(0xc); | 35 | u32_le version; |
| 36 | INSERT_PADDING_WORDS(1); | ||
| 37 | u8 flags; | ||
| 36 | std::array<NsoSegmentHeader, 3> segments; // Text, RoData, Data (in that order) | 38 | std::array<NsoSegmentHeader, 3> segments; // Text, RoData, Data (in that order) |
| 37 | u32_le bss_size; | 39 | u32_le bss_size; |
| 38 | INSERT_PADDING_BYTES(0x1c); | 40 | INSERT_PADDING_BYTES(0x1c); |
| 39 | std::array<u32_le, 3> segments_compressed_size; | 41 | std::array<u32_le, 3> segments_compressed_size; |
| 42 | |||
| 43 | bool IsSegmentCompressed(size_t segment_num) const { | ||
| 44 | ASSERT_MSG(segment_num < 3, "Invalid segment {}", segment_num); | ||
| 45 | return ((flags >> segment_num) & 1); | ||
| 46 | } | ||
| 40 | }; | 47 | }; |
| 41 | static_assert(sizeof(NsoHeader) == 0x6c, "NsoHeader has incorrect size."); | 48 | static_assert(sizeof(NsoHeader) == 0x6c, "NsoHeader has incorrect size."); |
| 42 | static_assert(std::is_trivially_copyable_v<NsoHeader>, "NsoHeader isn't trivially copyable."); | 49 | static_assert(std::is_trivially_copyable_v<NsoHeader>, "NsoHeader isn't trivially copyable."); |
| @@ -105,9 +112,11 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base) { | |||
| 105 | Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create(kernel, ""); | 112 | Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create(kernel, ""); |
| 106 | std::vector<u8> program_image; | 113 | std::vector<u8> program_image; |
| 107 | for (std::size_t i = 0; i < nso_header.segments.size(); ++i) { | 114 | for (std::size_t i = 0; i < nso_header.segments.size(); ++i) { |
| 108 | const std::vector<u8> compressed_data = | 115 | std::vector<u8> data = |
| 109 | file->ReadBytes(nso_header.segments_compressed_size[i], nso_header.segments[i].offset); | 116 | file->ReadBytes(nso_header.segments_compressed_size[i], nso_header.segments[i].offset); |
| 110 | std::vector<u8> data = DecompressSegment(compressed_data, nso_header.segments[i]); | 117 | if (nso_header.IsSegmentCompressed(i)) { |
| 118 | data = DecompressSegment(data, nso_header.segments[i]); | ||
| 119 | } | ||
| 111 | program_image.resize(nso_header.segments[i].location); | 120 | program_image.resize(nso_header.segments[i].location); |
| 112 | program_image.insert(program_image.end(), data.begin(), data.end()); | 121 | program_image.insert(program_image.end(), data.begin(), data.end()); |
| 113 | codeset->segments[i].addr = nso_header.segments[i].location; | 122 | codeset->segments[i].addr = nso_header.segments[i].location; |
| @@ -144,21 +153,17 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base) { | |||
| 144 | return load_base + image_size; | 153 | return load_base + image_size; |
| 145 | } | 154 | } |
| 146 | 155 | ||
| 147 | ResultStatus AppLoader_NSO::Load(Kernel::SharedPtr<Kernel::Process>& process) { | 156 | ResultStatus AppLoader_NSO::Load(Kernel::Process& process) { |
| 148 | if (is_loaded) { | 157 | if (is_loaded) { |
| 149 | return ResultStatus::ErrorAlreadyLoaded; | 158 | return ResultStatus::ErrorAlreadyLoaded; |
| 150 | } | 159 | } |
| 151 | 160 | ||
| 152 | // Load module | 161 | // Load module |
| 153 | LoadModule(file, Memory::PROCESS_IMAGE_VADDR); | 162 | const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); |
| 154 | LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), Memory::PROCESS_IMAGE_VADDR); | 163 | LoadModule(file, base_address); |
| 164 | LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address); | ||
| 155 | 165 | ||
| 156 | auto& kernel = Core::System::GetInstance().Kernel(); | 166 | process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE); |
| 157 | process->svc_access_mask.set(); | ||
| 158 | process->resource_limit = | ||
| 159 | kernel.ResourceLimitForCategory(Kernel::ResourceLimitCategory::APPLICATION); | ||
| 160 | process->Run(Memory::PROCESS_IMAGE_VADDR, Kernel::THREADPRIO_DEFAULT, | ||
| 161 | Memory::DEFAULT_STACK_SIZE); | ||
| 162 | 167 | ||
| 163 | is_loaded = true; | 168 | is_loaded = true; |
| 164 | return ResultStatus::Success; | 169 | return ResultStatus::Success; |
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h index aaeb1f2a9..7f142405b 100644 --- a/src/core/loader/nso.h +++ b/src/core/loader/nso.h | |||
| @@ -4,9 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <string> | ||
| 8 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 9 | #include "core/hle/kernel/object.h" | ||
| 10 | #include "core/loader/linker.h" | 8 | #include "core/loader/linker.h" |
| 11 | #include "core/loader/loader.h" | 9 | #include "core/loader/loader.h" |
| 12 | 10 | ||
| @@ -30,7 +28,7 @@ public: | |||
| 30 | 28 | ||
| 31 | static VAddr LoadModule(FileSys::VirtualFile file, VAddr load_base); | 29 | static VAddr LoadModule(FileSys::VirtualFile file, VAddr load_base); |
| 32 | 30 | ||
| 33 | ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; | 31 | ResultStatus Load(Kernel::Process& process) override; |
| 34 | }; | 32 | }; |
| 35 | 33 | ||
| 36 | } // namespace Loader | 34 | } // namespace Loader |
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp index 291a9876d..b7ba77ef4 100644 --- a/src/core/loader/nsp.cpp +++ b/src/core/loader/nsp.cpp | |||
| @@ -10,8 +10,6 @@ | |||
| 10 | #include "core/file_sys/control_metadata.h" | 10 | #include "core/file_sys/control_metadata.h" |
| 11 | #include "core/file_sys/nca_metadata.h" | 11 | #include "core/file_sys/nca_metadata.h" |
| 12 | #include "core/file_sys/patch_manager.h" | 12 | #include "core/file_sys/patch_manager.h" |
| 13 | #include "core/file_sys/registered_cache.h" | ||
| 14 | #include "core/file_sys/romfs.h" | ||
| 15 | #include "core/file_sys/submission_package.h" | 13 | #include "core/file_sys/submission_package.h" |
| 16 | #include "core/hle/kernel/process.h" | 14 | #include "core/hle/kernel/process.h" |
| 17 | #include "core/loader/deconstructed_rom_directory.h" | 15 | #include "core/loader/deconstructed_rom_directory.h" |
| @@ -62,7 +60,7 @@ FileType AppLoader_NSP::IdentifyType(const FileSys::VirtualFile& file) { | |||
| 62 | return FileType::Error; | 60 | return FileType::Error; |
| 63 | } | 61 | } |
| 64 | 62 | ||
| 65 | ResultStatus AppLoader_NSP::Load(Kernel::SharedPtr<Kernel::Process>& process) { | 63 | ResultStatus AppLoader_NSP::Load(Kernel::Process& process) { |
| 66 | if (is_loaded) { | 64 | if (is_loaded) { |
| 67 | return ResultStatus::ErrorAlreadyLoaded; | 65 | return ResultStatus::ErrorAlreadyLoaded; |
| 68 | } | 66 | } |
diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h index 7ef810499..eac9b819a 100644 --- a/src/core/loader/nsp.h +++ b/src/core/loader/nsp.h | |||
| @@ -35,7 +35,7 @@ public: | |||
| 35 | return IdentifyType(file); | 35 | return IdentifyType(file); |
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; | 38 | ResultStatus Load(Kernel::Process& process) override; |
| 39 | 39 | ||
| 40 | ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; | 40 | ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; |
| 41 | ResultStatus ReadProgramId(u64& out_program_id) override; | 41 | ResultStatus ReadProgramId(u64& out_program_id) override; |
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp index 16509229f..eda67a8c8 100644 --- a/src/core/loader/xci.cpp +++ b/src/core/loader/xci.cpp | |||
| @@ -9,8 +9,6 @@ | |||
| 9 | #include "core/file_sys/content_archive.h" | 9 | #include "core/file_sys/content_archive.h" |
| 10 | #include "core/file_sys/control_metadata.h" | 10 | #include "core/file_sys/control_metadata.h" |
| 11 | #include "core/file_sys/patch_manager.h" | 11 | #include "core/file_sys/patch_manager.h" |
| 12 | #include "core/file_sys/romfs.h" | ||
| 13 | #include "core/file_sys/submission_package.h" | ||
| 14 | #include "core/hle/kernel/process.h" | 12 | #include "core/hle/kernel/process.h" |
| 15 | #include "core/loader/nca.h" | 13 | #include "core/loader/nca.h" |
| 16 | #include "core/loader/xci.h" | 14 | #include "core/loader/xci.h" |
| @@ -46,7 +44,7 @@ FileType AppLoader_XCI::IdentifyType(const FileSys::VirtualFile& file) { | |||
| 46 | return FileType::Error; | 44 | return FileType::Error; |
| 47 | } | 45 | } |
| 48 | 46 | ||
| 49 | ResultStatus AppLoader_XCI::Load(Kernel::SharedPtr<Kernel::Process>& process) { | 47 | ResultStatus AppLoader_XCI::Load(Kernel::Process& process) { |
| 50 | if (is_loaded) { | 48 | if (is_loaded) { |
| 51 | return ResultStatus::ErrorAlreadyLoaded; | 49 | return ResultStatus::ErrorAlreadyLoaded; |
| 52 | } | 50 | } |
diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h index cc4287e17..17e47b658 100644 --- a/src/core/loader/xci.h +++ b/src/core/loader/xci.h | |||
| @@ -35,7 +35,7 @@ public: | |||
| 35 | return IdentifyType(file); | 35 | return IdentifyType(file); |
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; | 38 | ResultStatus Load(Kernel::Process& process) override; |
| 39 | 39 | ||
| 40 | ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; | 40 | ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; |
| 41 | ResultStatus ReadProgramId(u64& out_program_id) override; | 41 | ResultStatus ReadProgramId(u64& out_program_id) override; |
diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 0e4e0157c..014298ed6 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp | |||
| @@ -3,7 +3,6 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <array> | ||
| 7 | #include <cstring> | 6 | #include <cstring> |
| 8 | #include <utility> | 7 | #include <utility> |
| 9 | 8 | ||
| @@ -15,11 +14,11 @@ | |||
| 15 | #include "core/arm/arm_interface.h" | 14 | #include "core/arm/arm_interface.h" |
| 16 | #include "core/core.h" | 15 | #include "core/core.h" |
| 17 | #include "core/hle/kernel/process.h" | 16 | #include "core/hle/kernel/process.h" |
| 17 | #include "core/hle/kernel/vm_manager.h" | ||
| 18 | #include "core/hle/lock.h" | 18 | #include "core/hle/lock.h" |
| 19 | #include "core/memory.h" | 19 | #include "core/memory.h" |
| 20 | #include "core/memory_setup.h" | 20 | #include "core/memory_setup.h" |
| 21 | #include "video_core/renderer_base.h" | 21 | #include "video_core/renderer_base.h" |
| 22 | #include "video_core/video_core.h" | ||
| 23 | 22 | ||
| 24 | namespace Memory { | 23 | namespace Memory { |
| 25 | 24 | ||
| @@ -41,6 +40,21 @@ PageTable* GetCurrentPageTable() { | |||
| 41 | return current_page_table; | 40 | return current_page_table; |
| 42 | } | 41 | } |
| 43 | 42 | ||
| 43 | PageTable::PageTable() = default; | ||
| 44 | |||
| 45 | PageTable::PageTable(std::size_t address_space_width_in_bits) { | ||
| 46 | Resize(address_space_width_in_bits); | ||
| 47 | } | ||
| 48 | |||
| 49 | PageTable::~PageTable() = default; | ||
| 50 | |||
| 51 | void PageTable::Resize(std::size_t address_space_width_in_bits) { | ||
| 52 | const std::size_t num_page_table_entries = 1ULL << (address_space_width_in_bits - PAGE_BITS); | ||
| 53 | |||
| 54 | pointers.resize(num_page_table_entries); | ||
| 55 | attributes.resize(num_page_table_entries); | ||
| 56 | } | ||
| 57 | |||
| 44 | static void MapPages(PageTable& page_table, VAddr base, u64 size, u8* memory, PageType type) { | 58 | static void MapPages(PageTable& page_table, VAddr base, u64 size, u8* memory, PageType type) { |
| 45 | LOG_DEBUG(HW_Memory, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * PAGE_SIZE, | 59 | LOG_DEBUG(HW_Memory, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * PAGE_SIZE, |
| 46 | (base + size) * PAGE_SIZE); | 60 | (base + size) * PAGE_SIZE); |
| @@ -50,7 +64,7 @@ static void MapPages(PageTable& page_table, VAddr base, u64 size, u8* memory, Pa | |||
| 50 | 64 | ||
| 51 | VAddr end = base + size; | 65 | VAddr end = base + size; |
| 52 | while (base != end) { | 66 | while (base != end) { |
| 53 | ASSERT_MSG(base < PAGE_TABLE_NUM_ENTRIES, "out of range mapping at {:016X}", base); | 67 | ASSERT_MSG(base < page_table.pointers.size(), "out of range mapping at {:016X}", base); |
| 54 | 68 | ||
| 55 | page_table.attributes[base] = type; | 69 | page_table.attributes[base] = type; |
| 56 | page_table.pointers[base] = memory; | 70 | page_table.pointers[base] = memory; |
| @@ -105,7 +119,7 @@ void RemoveDebugHook(PageTable& page_table, VAddr base, u64 size, MemoryHookPoin | |||
| 105 | static u8* GetPointerFromVMA(const Kernel::Process& process, VAddr vaddr) { | 119 | static u8* GetPointerFromVMA(const Kernel::Process& process, VAddr vaddr) { |
| 106 | u8* direct_pointer = nullptr; | 120 | u8* direct_pointer = nullptr; |
| 107 | 121 | ||
| 108 | auto& vm_manager = process.vm_manager; | 122 | auto& vm_manager = process.VMManager(); |
| 109 | 123 | ||
| 110 | auto it = vm_manager.FindVMA(vaddr); | 124 | auto it = vm_manager.FindVMA(vaddr); |
| 111 | ASSERT(it != vm_manager.vma_map.end()); | 125 | ASSERT(it != vm_manager.vma_map.end()); |
| @@ -200,7 +214,7 @@ void Write(const VAddr vaddr, const T data) { | |||
| 200 | } | 214 | } |
| 201 | 215 | ||
| 202 | bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) { | 216 | bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) { |
| 203 | auto& page_table = process.vm_manager.page_table; | 217 | const auto& page_table = process.VMManager().page_table; |
| 204 | 218 | ||
| 205 | const u8* page_pointer = page_table.pointers[vaddr >> PAGE_BITS]; | 219 | const u8* page_pointer = page_table.pointers[vaddr >> PAGE_BITS]; |
| 206 | if (page_pointer) | 220 | if (page_pointer) |
| @@ -323,7 +337,7 @@ void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode) { | |||
| 323 | return; | 337 | return; |
| 324 | } | 338 | } |
| 325 | 339 | ||
| 326 | VAddr end = start + size; | 340 | const VAddr end = start + size; |
| 327 | 341 | ||
| 328 | const auto CheckRegion = [&](VAddr region_start, VAddr region_end) { | 342 | const auto CheckRegion = [&](VAddr region_start, VAddr region_end) { |
| 329 | if (start >= region_end || end <= region_start) { | 343 | if (start >= region_end || end <= region_start) { |
| @@ -333,7 +347,7 @@ void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode) { | |||
| 333 | 347 | ||
| 334 | const VAddr overlap_start = std::max(start, region_start); | 348 | const VAddr overlap_start = std::max(start, region_start); |
| 335 | const VAddr overlap_end = std::min(end, region_end); | 349 | const VAddr overlap_end = std::min(end, region_end); |
| 336 | const u64 overlap_size = overlap_end - overlap_start; | 350 | const VAddr overlap_size = overlap_end - overlap_start; |
| 337 | 351 | ||
| 338 | auto& rasterizer = system_instance.Renderer().Rasterizer(); | 352 | auto& rasterizer = system_instance.Renderer().Rasterizer(); |
| 339 | switch (mode) { | 353 | switch (mode) { |
| @@ -349,8 +363,10 @@ void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode) { | |||
| 349 | } | 363 | } |
| 350 | }; | 364 | }; |
| 351 | 365 | ||
| 352 | CheckRegion(PROCESS_IMAGE_VADDR, PROCESS_IMAGE_VADDR_END); | 366 | const auto& vm_manager = Core::CurrentProcess()->VMManager(); |
| 353 | CheckRegion(HEAP_VADDR, HEAP_VADDR_END); | 367 | |
| 368 | CheckRegion(vm_manager.GetCodeRegionBaseAddress(), vm_manager.GetCodeRegionEndAddress()); | ||
| 369 | CheckRegion(vm_manager.GetHeapRegionBaseAddress(), vm_manager.GetHeapRegionEndAddress()); | ||
| 354 | } | 370 | } |
| 355 | 371 | ||
| 356 | u8 Read8(const VAddr addr) { | 372 | u8 Read8(const VAddr addr) { |
| @@ -370,16 +386,16 @@ u64 Read64(const VAddr addr) { | |||
| 370 | } | 386 | } |
| 371 | 387 | ||
| 372 | void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer, | 388 | void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer, |
| 373 | const size_t size) { | 389 | const std::size_t size) { |
| 374 | auto& page_table = process.vm_manager.page_table; | 390 | const auto& page_table = process.VMManager().page_table; |
| 375 | 391 | ||
| 376 | size_t remaining_size = size; | 392 | std::size_t remaining_size = size; |
| 377 | size_t page_index = src_addr >> PAGE_BITS; | 393 | std::size_t page_index = src_addr >> PAGE_BITS; |
| 378 | size_t page_offset = src_addr & PAGE_MASK; | 394 | std::size_t page_offset = src_addr & PAGE_MASK; |
| 379 | 395 | ||
| 380 | while (remaining_size > 0) { | 396 | while (remaining_size > 0) { |
| 381 | const size_t copy_amount = | 397 | const std::size_t copy_amount = |
| 382 | std::min(static_cast<size_t>(PAGE_SIZE) - page_offset, remaining_size); | 398 | std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); |
| 383 | const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); | 399 | const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); |
| 384 | 400 | ||
| 385 | switch (page_table.attributes[page_index]) { | 401 | switch (page_table.attributes[page_index]) { |
| @@ -414,7 +430,7 @@ void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_ | |||
| 414 | } | 430 | } |
| 415 | } | 431 | } |
| 416 | 432 | ||
| 417 | void ReadBlock(const VAddr src_addr, void* dest_buffer, const size_t size) { | 433 | void ReadBlock(const VAddr src_addr, void* dest_buffer, const std::size_t size) { |
| 418 | ReadBlock(*Core::CurrentProcess(), src_addr, dest_buffer, size); | 434 | ReadBlock(*Core::CurrentProcess(), src_addr, dest_buffer, size); |
| 419 | } | 435 | } |
| 420 | 436 | ||
| @@ -435,15 +451,15 @@ void Write64(const VAddr addr, const u64 data) { | |||
| 435 | } | 451 | } |
| 436 | 452 | ||
| 437 | void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer, | 453 | void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer, |
| 438 | const size_t size) { | 454 | const std::size_t size) { |
| 439 | auto& page_table = process.vm_manager.page_table; | 455 | const auto& page_table = process.VMManager().page_table; |
| 440 | size_t remaining_size = size; | 456 | std::size_t remaining_size = size; |
| 441 | size_t page_index = dest_addr >> PAGE_BITS; | 457 | std::size_t page_index = dest_addr >> PAGE_BITS; |
| 442 | size_t page_offset = dest_addr & PAGE_MASK; | 458 | std::size_t page_offset = dest_addr & PAGE_MASK; |
| 443 | 459 | ||
| 444 | while (remaining_size > 0) { | 460 | while (remaining_size > 0) { |
| 445 | const size_t copy_amount = | 461 | const std::size_t copy_amount = |
| 446 | std::min(static_cast<size_t>(PAGE_SIZE) - page_offset, remaining_size); | 462 | std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); |
| 447 | const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); | 463 | const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); |
| 448 | 464 | ||
| 449 | switch (page_table.attributes[page_index]) { | 465 | switch (page_table.attributes[page_index]) { |
| @@ -477,19 +493,19 @@ void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const voi | |||
| 477 | } | 493 | } |
| 478 | } | 494 | } |
| 479 | 495 | ||
| 480 | void WriteBlock(const VAddr dest_addr, const void* src_buffer, const size_t size) { | 496 | void WriteBlock(const VAddr dest_addr, const void* src_buffer, const std::size_t size) { |
| 481 | WriteBlock(*Core::CurrentProcess(), dest_addr, src_buffer, size); | 497 | WriteBlock(*Core::CurrentProcess(), dest_addr, src_buffer, size); |
| 482 | } | 498 | } |
| 483 | 499 | ||
| 484 | void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const size_t size) { | 500 | void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const std::size_t size) { |
| 485 | auto& page_table = process.vm_manager.page_table; | 501 | const auto& page_table = process.VMManager().page_table; |
| 486 | size_t remaining_size = size; | 502 | std::size_t remaining_size = size; |
| 487 | size_t page_index = dest_addr >> PAGE_BITS; | 503 | std::size_t page_index = dest_addr >> PAGE_BITS; |
| 488 | size_t page_offset = dest_addr & PAGE_MASK; | 504 | std::size_t page_offset = dest_addr & PAGE_MASK; |
| 489 | 505 | ||
| 490 | while (remaining_size > 0) { | 506 | while (remaining_size > 0) { |
| 491 | const size_t copy_amount = | 507 | const std::size_t copy_amount = |
| 492 | std::min(static_cast<size_t>(PAGE_SIZE) - page_offset, remaining_size); | 508 | std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); |
| 493 | const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); | 509 | const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); |
| 494 | 510 | ||
| 495 | switch (page_table.attributes[page_index]) { | 511 | switch (page_table.attributes[page_index]) { |
| @@ -522,15 +538,16 @@ void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const size | |||
| 522 | } | 538 | } |
| 523 | } | 539 | } |
| 524 | 540 | ||
| 525 | void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr, const size_t size) { | 541 | void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr, |
| 526 | auto& page_table = process.vm_manager.page_table; | 542 | const std::size_t size) { |
| 527 | size_t remaining_size = size; | 543 | const auto& page_table = process.VMManager().page_table; |
| 528 | size_t page_index = src_addr >> PAGE_BITS; | 544 | std::size_t remaining_size = size; |
| 529 | size_t page_offset = src_addr & PAGE_MASK; | 545 | std::size_t page_index = src_addr >> PAGE_BITS; |
| 546 | std::size_t page_offset = src_addr & PAGE_MASK; | ||
| 530 | 547 | ||
| 531 | while (remaining_size > 0) { | 548 | while (remaining_size > 0) { |
| 532 | const size_t copy_amount = | 549 | const std::size_t copy_amount = |
| 533 | std::min(static_cast<size_t>(PAGE_SIZE) - page_offset, remaining_size); | 550 | std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size); |
| 534 | const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); | 551 | const VAddr current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset); |
| 535 | 552 | ||
| 536 | switch (page_table.attributes[page_index]) { | 553 | switch (page_table.attributes[page_index]) { |
| @@ -565,7 +582,7 @@ void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr, | |||
| 565 | } | 582 | } |
| 566 | } | 583 | } |
| 567 | 584 | ||
| 568 | void CopyBlock(VAddr dest_addr, VAddr src_addr, size_t size) { | 585 | void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size) { |
| 569 | CopyBlock(*Core::CurrentProcess(), dest_addr, src_addr, size); | 586 | CopyBlock(*Core::CurrentProcess(), dest_addr, src_addr, size); |
| 570 | } | 587 | } |
| 571 | 588 | ||
diff --git a/src/core/memory.h b/src/core/memory.h index f06e04a75..1acf5ce8c 100644 --- a/src/core/memory.h +++ b/src/core/memory.h | |||
| @@ -4,10 +4,10 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | ||
| 8 | #include <cstddef> | 7 | #include <cstddef> |
| 9 | #include <string> | 8 | #include <string> |
| 10 | #include <tuple> | 9 | #include <tuple> |
| 10 | #include <vector> | ||
| 11 | #include <boost/icl/interval_map.hpp> | 11 | #include <boost/icl/interval_map.hpp> |
| 12 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| 13 | #include "core/memory_hook.h" | 13 | #include "core/memory_hook.h" |
| @@ -22,11 +22,9 @@ namespace Memory { | |||
| 22 | * Page size used by the ARM architecture. This is the smallest granularity with which memory can | 22 | * Page size used by the ARM architecture. This is the smallest granularity with which memory can |
| 23 | * be mapped. | 23 | * be mapped. |
| 24 | */ | 24 | */ |
| 25 | constexpr size_t PAGE_BITS = 12; | 25 | constexpr std::size_t PAGE_BITS = 12; |
| 26 | constexpr u64 PAGE_SIZE = 1 << PAGE_BITS; | 26 | constexpr u64 PAGE_SIZE = 1ULL << PAGE_BITS; |
| 27 | constexpr u64 PAGE_MASK = PAGE_SIZE - 1; | 27 | constexpr u64 PAGE_MASK = PAGE_SIZE - 1; |
| 28 | constexpr size_t ADDRESS_SPACE_BITS = 36; | ||
| 29 | constexpr size_t PAGE_TABLE_NUM_ENTRIES = 1ULL << (ADDRESS_SPACE_BITS - PAGE_BITS); | ||
| 30 | 28 | ||
| 31 | enum class PageType : u8 { | 29 | enum class PageType : u8 { |
| 32 | /// Page is unmapped and should cause an access error. | 30 | /// Page is unmapped and should cause an access error. |
| @@ -62,32 +60,39 @@ struct SpecialRegion { | |||
| 62 | * mimics the way a real CPU page table works. | 60 | * mimics the way a real CPU page table works. |
| 63 | */ | 61 | */ |
| 64 | struct PageTable { | 62 | struct PageTable { |
| 63 | explicit PageTable(); | ||
| 64 | explicit PageTable(std::size_t address_space_width_in_bits); | ||
| 65 | ~PageTable(); | ||
| 66 | |||
| 67 | /** | ||
| 68 | * Resizes the page table to be able to accomodate enough pages within | ||
| 69 | * a given address space. | ||
| 70 | * | ||
| 71 | * @param address_space_width_in_bits The address size width in bits. | ||
| 72 | */ | ||
| 73 | void Resize(std::size_t address_space_width_in_bits); | ||
| 74 | |||
| 65 | /** | 75 | /** |
| 66 | * Array of memory pointers backing each page. An entry can only be non-null if the | 76 | * Vector of memory pointers backing each page. An entry can only be non-null if the |
| 67 | * corresponding entry in the `attributes` array is of type `Memory`. | 77 | * corresponding entry in the `attributes` vector is of type `Memory`. |
| 68 | */ | 78 | */ |
| 69 | std::array<u8*, PAGE_TABLE_NUM_ENTRIES> pointers; | 79 | std::vector<u8*> pointers; |
| 70 | 80 | ||
| 71 | /** | 81 | /** |
| 72 | * Contains MMIO handlers that back memory regions whose entries in the `attribute` array is of | 82 | * Contains MMIO handlers that back memory regions whose entries in the `attribute` vector is |
| 73 | * type `Special`. | 83 | * of type `Special`. |
| 74 | */ | 84 | */ |
| 75 | boost::icl::interval_map<VAddr, std::set<SpecialRegion>> special_regions; | 85 | boost::icl::interval_map<VAddr, std::set<SpecialRegion>> special_regions; |
| 76 | 86 | ||
| 77 | /** | 87 | /** |
| 78 | * Array of fine grained page attributes. If it is set to any value other than `Memory`, then | 88 | * Vector of fine grained page attributes. If it is set to any value other than `Memory`, then |
| 79 | * the corresponding entry in `pointers` MUST be set to null. | 89 | * the corresponding entry in `pointers` MUST be set to null. |
| 80 | */ | 90 | */ |
| 81 | std::array<PageType, PAGE_TABLE_NUM_ENTRIES> attributes; | 91 | std::vector<PageType> attributes; |
| 82 | }; | 92 | }; |
| 83 | 93 | ||
| 84 | /// Virtual user-space memory regions | 94 | /// Virtual user-space memory regions |
| 85 | enum : VAddr { | 95 | enum : VAddr { |
| 86 | /// Where the application text, data and bss reside. | ||
| 87 | PROCESS_IMAGE_VADDR = 0x08000000, | ||
| 88 | PROCESS_IMAGE_MAX_SIZE = 0x08000000, | ||
| 89 | PROCESS_IMAGE_VADDR_END = PROCESS_IMAGE_VADDR + PROCESS_IMAGE_MAX_SIZE, | ||
| 90 | |||
| 91 | /// Read-only page containing kernel and system configuration values. | 96 | /// Read-only page containing kernel and system configuration values. |
| 92 | CONFIG_MEMORY_VADDR = 0x1FF80000, | 97 | CONFIG_MEMORY_VADDR = 0x1FF80000, |
| 93 | CONFIG_MEMORY_SIZE = 0x00001000, | 98 | CONFIG_MEMORY_SIZE = 0x00001000, |
| @@ -98,36 +103,12 @@ enum : VAddr { | |||
| 98 | SHARED_PAGE_SIZE = 0x00001000, | 103 | SHARED_PAGE_SIZE = 0x00001000, |
| 99 | SHARED_PAGE_VADDR_END = SHARED_PAGE_VADDR + SHARED_PAGE_SIZE, | 104 | SHARED_PAGE_VADDR_END = SHARED_PAGE_VADDR + SHARED_PAGE_SIZE, |
| 100 | 105 | ||
| 101 | /// Area where TLS (Thread-Local Storage) buffers are allocated. | 106 | /// TLS (Thread-Local Storage) related. |
| 102 | TLS_AREA_VADDR = 0x40000000, | ||
| 103 | TLS_ENTRY_SIZE = 0x200, | 107 | TLS_ENTRY_SIZE = 0x200, |
| 104 | TLS_AREA_SIZE = 0x10000000, | ||
| 105 | TLS_AREA_VADDR_END = TLS_AREA_VADDR + TLS_AREA_SIZE, | ||
| 106 | 108 | ||
| 107 | /// Application stack | 109 | /// Application stack |
| 108 | STACK_AREA_VADDR = TLS_AREA_VADDR_END, | ||
| 109 | STACK_AREA_SIZE = 0x10000000, | ||
| 110 | STACK_AREA_VADDR_END = STACK_AREA_VADDR + STACK_AREA_SIZE, | ||
| 111 | DEFAULT_STACK_SIZE = 0x100000, | 110 | DEFAULT_STACK_SIZE = 0x100000, |
| 112 | 111 | ||
| 113 | /// Application heap | ||
| 114 | /// Size is confirmed to be a static value on fw 3.0.0 | ||
| 115 | HEAP_VADDR = 0x108000000, | ||
| 116 | HEAP_SIZE = 0x180000000, | ||
| 117 | HEAP_VADDR_END = HEAP_VADDR + HEAP_SIZE, | ||
| 118 | |||
| 119 | /// New map region | ||
| 120 | /// Size is confirmed to be a static value on fw 3.0.0 | ||
| 121 | NEW_MAP_REGION_VADDR = HEAP_VADDR_END, | ||
| 122 | NEW_MAP_REGION_SIZE = 0x80000000, | ||
| 123 | NEW_MAP_REGION_VADDR_END = NEW_MAP_REGION_VADDR + NEW_MAP_REGION_SIZE, | ||
| 124 | |||
| 125 | /// Map region | ||
| 126 | /// Size is confirmed to be a static value on fw 3.0.0 | ||
| 127 | MAP_REGION_VADDR = NEW_MAP_REGION_VADDR_END, | ||
| 128 | MAP_REGION_SIZE = 0x1000000000, | ||
| 129 | MAP_REGION_VADDR_END = MAP_REGION_VADDR + MAP_REGION_SIZE, | ||
| 130 | |||
| 131 | /// Kernel Virtual Address Range | 112 | /// Kernel Virtual Address Range |
| 132 | KERNEL_REGION_VADDR = 0xFFFFFF8000000000, | 113 | KERNEL_REGION_VADDR = 0xFFFFFF8000000000, |
| 133 | KERNEL_REGION_SIZE = 0x7FFFE00000, | 114 | KERNEL_REGION_SIZE = 0x7FFFE00000, |
| @@ -154,13 +135,13 @@ void Write16(VAddr addr, u16 data); | |||
| 154 | void Write32(VAddr addr, u32 data); | 135 | void Write32(VAddr addr, u32 data); |
| 155 | void Write64(VAddr addr, u64 data); | 136 | void Write64(VAddr addr, u64 data); |
| 156 | 137 | ||
| 157 | void ReadBlock(const Kernel::Process& process, VAddr src_addr, void* dest_buffer, size_t size); | 138 | void ReadBlock(const Kernel::Process& process, VAddr src_addr, void* dest_buffer, std::size_t size); |
| 158 | void ReadBlock(VAddr src_addr, void* dest_buffer, size_t size); | 139 | void ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size); |
| 159 | void WriteBlock(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer, | 140 | void WriteBlock(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer, |
| 160 | size_t size); | 141 | std::size_t size); |
| 161 | void WriteBlock(VAddr dest_addr, const void* src_buffer, size_t size); | 142 | void WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size); |
| 162 | void ZeroBlock(const Kernel::Process& process, VAddr dest_addr, size_t size); | 143 | void ZeroBlock(const Kernel::Process& process, VAddr dest_addr, std::size_t size); |
| 163 | void CopyBlock(VAddr dest_addr, VAddr src_addr, size_t size); | 144 | void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size); |
| 164 | 145 | ||
| 165 | u8* GetPointer(VAddr vaddr); | 146 | u8* GetPointer(VAddr vaddr); |
| 166 | 147 | ||
diff --git a/src/core/memory_hook.h b/src/core/memory_hook.h index e8ea19333..0269c7ff1 100644 --- a/src/core/memory_hook.h +++ b/src/core/memory_hook.h | |||
| @@ -32,14 +32,14 @@ public: | |||
| 32 | virtual boost::optional<u32> Read32(VAddr addr) = 0; | 32 | virtual boost::optional<u32> Read32(VAddr addr) = 0; |
| 33 | virtual boost::optional<u64> Read64(VAddr addr) = 0; | 33 | virtual boost::optional<u64> Read64(VAddr addr) = 0; |
| 34 | 34 | ||
| 35 | virtual bool ReadBlock(VAddr src_addr, void* dest_buffer, size_t size) = 0; | 35 | virtual bool ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) = 0; |
| 36 | 36 | ||
| 37 | virtual bool Write8(VAddr addr, u8 data) = 0; | 37 | virtual bool Write8(VAddr addr, u8 data) = 0; |
| 38 | virtual bool Write16(VAddr addr, u16 data) = 0; | 38 | virtual bool Write16(VAddr addr, u16 data) = 0; |
| 39 | virtual bool Write32(VAddr addr, u32 data) = 0; | 39 | virtual bool Write32(VAddr addr, u32 data) = 0; |
| 40 | virtual bool Write64(VAddr addr, u64 data) = 0; | 40 | virtual bool Write64(VAddr addr, u64 data) = 0; |
| 41 | 41 | ||
| 42 | virtual bool WriteBlock(VAddr dest_addr, const void* src_buffer, size_t size) = 0; | 42 | virtual bool WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size) = 0; |
| 43 | }; | 43 | }; |
| 44 | 44 | ||
| 45 | using MemoryHookPointer = std::shared_ptr<MemoryHook>; | 45 | using MemoryHookPointer = std::shared_ptr<MemoryHook>; |
diff --git a/src/core/tracer/recorder.cpp b/src/core/tracer/recorder.cpp index af032f0c9..73cacb47f 100644 --- a/src/core/tracer/recorder.cpp +++ b/src/core/tracer/recorder.cpp | |||
| @@ -76,7 +76,7 @@ void Recorder::Finish(const std::string& filename) { | |||
| 76 | try { | 76 | try { |
| 77 | // Open file and write header | 77 | // Open file and write header |
| 78 | FileUtil::IOFile file(filename, "wb"); | 78 | FileUtil::IOFile file(filename, "wb"); |
| 79 | size_t written = file.WriteObject(header); | 79 | std::size_t written = file.WriteObject(header); |
| 80 | if (written != 1 || file.Tell() != initial.gpu_registers) | 80 | if (written != 1 || file.Tell() != initial.gpu_registers) |
| 81 | throw "Failed to write header"; | 81 | throw "Failed to write header"; |
| 82 | 82 | ||
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 4e75a72ec..37f09ce5f 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt | |||
| @@ -4,14 +4,12 @@ add_executable(tests | |||
| 4 | core/arm/arm_test_common.cpp | 4 | core/arm/arm_test_common.cpp |
| 5 | core/arm/arm_test_common.h | 5 | core/arm/arm_test_common.h |
| 6 | core/core_timing.cpp | 6 | core/core_timing.cpp |
| 7 | glad.cpp | ||
| 8 | tests.cpp | 7 | tests.cpp |
| 9 | ) | 8 | ) |
| 10 | 9 | ||
| 11 | create_target_directory_groups(tests) | 10 | create_target_directory_groups(tests) |
| 12 | 11 | ||
| 13 | target_link_libraries(tests PRIVATE common core) | 12 | target_link_libraries(tests PRIVATE common core) |
| 14 | target_link_libraries(tests PRIVATE glad) # To support linker work-around | ||
| 15 | target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} catch-single-include Threads::Threads) | 13 | target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} catch-single-include Threads::Threads) |
| 16 | 14 | ||
| 17 | add_test(NAME tests COMMAND tests) | 15 | add_test(NAME tests COMMAND tests) |
diff --git a/src/tests/common/ring_buffer.cpp b/src/tests/common/ring_buffer.cpp index f3fe57839..c883c4d56 100644 --- a/src/tests/common/ring_buffer.cpp +++ b/src/tests/common/ring_buffer.cpp | |||
| @@ -17,9 +17,9 @@ TEST_CASE("RingBuffer: Basic Tests", "[common]") { | |||
| 17 | RingBuffer<char, 4, 1> buf; | 17 | RingBuffer<char, 4, 1> buf; |
| 18 | 18 | ||
| 19 | // Pushing values into a ring buffer with space should succeed. | 19 | // Pushing values into a ring buffer with space should succeed. |
| 20 | for (size_t i = 0; i < 4; i++) { | 20 | for (std::size_t i = 0; i < 4; i++) { |
| 21 | const char elem = static_cast<char>(i); | 21 | const char elem = static_cast<char>(i); |
| 22 | const size_t count = buf.Push(&elem, 1); | 22 | const std::size_t count = buf.Push(&elem, 1); |
| 23 | REQUIRE(count == 1); | 23 | REQUIRE(count == 1); |
| 24 | } | 24 | } |
| 25 | 25 | ||
| @@ -28,7 +28,7 @@ TEST_CASE("RingBuffer: Basic Tests", "[common]") { | |||
| 28 | // Pushing values into a full ring buffer should fail. | 28 | // Pushing values into a full ring buffer should fail. |
| 29 | { | 29 | { |
| 30 | const char elem = static_cast<char>(42); | 30 | const char elem = static_cast<char>(42); |
| 31 | const size_t count = buf.Push(&elem, 1); | 31 | const std::size_t count = buf.Push(&elem, 1); |
| 32 | REQUIRE(count == 0); | 32 | REQUIRE(count == 0); |
| 33 | } | 33 | } |
| 34 | 34 | ||
| @@ -57,7 +57,7 @@ TEST_CASE("RingBuffer: Basic Tests", "[common]") { | |||
| 57 | { | 57 | { |
| 58 | std::vector<char> to_push(6); | 58 | std::vector<char> to_push(6); |
| 59 | std::iota(to_push.begin(), to_push.end(), 88); | 59 | std::iota(to_push.begin(), to_push.end(), 88); |
| 60 | const size_t count = buf.Push(to_push); | 60 | const std::size_t count = buf.Push(to_push); |
| 61 | REQUIRE(count == 3); | 61 | REQUIRE(count == 3); |
| 62 | } | 62 | } |
| 63 | 63 | ||
| @@ -79,9 +79,9 @@ TEST_CASE("RingBuffer: Basic Tests", "[common]") { | |||
| 79 | TEST_CASE("RingBuffer: Threaded Test", "[common]") { | 79 | TEST_CASE("RingBuffer: Threaded Test", "[common]") { |
| 80 | RingBuffer<char, 4, 2> buf; | 80 | RingBuffer<char, 4, 2> buf; |
| 81 | const char seed = 42; | 81 | const char seed = 42; |
| 82 | const size_t count = 1000000; | 82 | const std::size_t count = 1000000; |
| 83 | size_t full = 0; | 83 | std::size_t full = 0; |
| 84 | size_t empty = 0; | 84 | std::size_t empty = 0; |
| 85 | 85 | ||
| 86 | const auto next_value = [](std::array<char, 2>& value) { | 86 | const auto next_value = [](std::array<char, 2>& value) { |
| 87 | value[0] += 1; | 87 | value[0] += 1; |
| @@ -90,9 +90,9 @@ TEST_CASE("RingBuffer: Threaded Test", "[common]") { | |||
| 90 | 90 | ||
| 91 | std::thread producer{[&] { | 91 | std::thread producer{[&] { |
| 92 | std::array<char, 2> value = {seed, seed}; | 92 | std::array<char, 2> value = {seed, seed}; |
| 93 | size_t i = 0; | 93 | std::size_t i = 0; |
| 94 | while (i < count) { | 94 | while (i < count) { |
| 95 | if (const size_t c = buf.Push(&value[0], 1); c > 0) { | 95 | if (const std::size_t c = buf.Push(&value[0], 1); c > 0) { |
| 96 | REQUIRE(c == 1); | 96 | REQUIRE(c == 1); |
| 97 | i++; | 97 | i++; |
| 98 | next_value(value); | 98 | next_value(value); |
| @@ -105,7 +105,7 @@ TEST_CASE("RingBuffer: Threaded Test", "[common]") { | |||
| 105 | 105 | ||
| 106 | std::thread consumer{[&] { | 106 | std::thread consumer{[&] { |
| 107 | std::array<char, 2> value = {seed, seed}; | 107 | std::array<char, 2> value = {seed, seed}; |
| 108 | size_t i = 0; | 108 | std::size_t i = 0; |
| 109 | while (i < count) { | 109 | while (i < count) { |
| 110 | if (const std::vector<char> v = buf.Pop(1); v.size() > 0) { | 110 | if (const std::vector<char> v = buf.Pop(1); v.size() > 0) { |
| 111 | REQUIRE(v.size() == 2); | 111 | REQUIRE(v.size() == 2); |
diff --git a/src/tests/core/arm/arm_test_common.cpp b/src/tests/core/arm/arm_test_common.cpp index 038d57b3a..c0a57e71f 100644 --- a/src/tests/core/arm/arm_test_common.cpp +++ b/src/tests/core/arm/arm_test_common.cpp | |||
| @@ -2,6 +2,8 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | ||
| 6 | |||
| 5 | #include "core/core.h" | 7 | #include "core/core.h" |
| 6 | #include "core/hle/kernel/process.h" | 8 | #include "core/hle/kernel/process.h" |
| 7 | #include "core/memory.h" | 9 | #include "core/memory.h" |
| @@ -14,11 +16,12 @@ TestEnvironment::TestEnvironment(bool mutable_memory_) | |||
| 14 | : mutable_memory(mutable_memory_), test_memory(std::make_shared<TestMemory>(this)) { | 16 | : mutable_memory(mutable_memory_), test_memory(std::make_shared<TestMemory>(this)) { |
| 15 | 17 | ||
| 16 | Core::CurrentProcess() = Kernel::Process::Create(kernel, ""); | 18 | Core::CurrentProcess() = Kernel::Process::Create(kernel, ""); |
| 17 | page_table = &Core::CurrentProcess()->vm_manager.page_table; | 19 | page_table = &Core::CurrentProcess()->VMManager().page_table; |
| 18 | 20 | ||
| 19 | page_table->pointers.fill(nullptr); | 21 | std::fill(page_table->pointers.begin(), page_table->pointers.end(), nullptr); |
| 20 | page_table->special_regions.clear(); | 22 | page_table->special_regions.clear(); |
| 21 | page_table->attributes.fill(Memory::PageType::Unmapped); | 23 | std::fill(page_table->attributes.begin(), page_table->attributes.end(), |
| 24 | Memory::PageType::Unmapped); | ||
| 22 | 25 | ||
| 23 | Memory::MapIoRegion(*page_table, 0x00000000, 0x80000000, test_memory); | 26 | Memory::MapIoRegion(*page_table, 0x00000000, 0x80000000, test_memory); |
| 24 | Memory::MapIoRegion(*page_table, 0x80000000, 0x80000000, test_memory); | 27 | Memory::MapIoRegion(*page_table, 0x80000000, 0x80000000, test_memory); |
| @@ -87,11 +90,11 @@ boost::optional<u64> TestEnvironment::TestMemory::Read64(VAddr addr) { | |||
| 87 | return *Read32(addr) | static_cast<u64>(*Read32(addr + 4)) << 32; | 90 | return *Read32(addr) | static_cast<u64>(*Read32(addr + 4)) << 32; |
| 88 | } | 91 | } |
| 89 | 92 | ||
| 90 | bool TestEnvironment::TestMemory::ReadBlock(VAddr src_addr, void* dest_buffer, size_t size) { | 93 | bool TestEnvironment::TestMemory::ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) { |
| 91 | VAddr addr = src_addr; | 94 | VAddr addr = src_addr; |
| 92 | u8* data = static_cast<u8*>(dest_buffer); | 95 | u8* data = static_cast<u8*>(dest_buffer); |
| 93 | 96 | ||
| 94 | for (size_t i = 0; i < size; i++, addr++, data++) { | 97 | for (std::size_t i = 0; i < size; i++, addr++, data++) { |
| 95 | *data = *Read8(addr); | 98 | *data = *Read8(addr); |
| 96 | } | 99 | } |
| 97 | 100 | ||
| @@ -126,11 +129,12 @@ bool TestEnvironment::TestMemory::Write64(VAddr addr, u64 data) { | |||
| 126 | return true; | 129 | return true; |
| 127 | } | 130 | } |
| 128 | 131 | ||
| 129 | bool TestEnvironment::TestMemory::WriteBlock(VAddr dest_addr, const void* src_buffer, size_t size) { | 132 | bool TestEnvironment::TestMemory::WriteBlock(VAddr dest_addr, const void* src_buffer, |
| 133 | std::size_t size) { | ||
| 130 | VAddr addr = dest_addr; | 134 | VAddr addr = dest_addr; |
| 131 | const u8* data = static_cast<const u8*>(src_buffer); | 135 | const u8* data = static_cast<const u8*>(src_buffer); |
| 132 | 136 | ||
| 133 | for (size_t i = 0; i < size; i++, addr++, data++) { | 137 | for (std::size_t i = 0; i < size; i++, addr++, data++) { |
| 134 | env->write_records.emplace_back(8, addr, *data); | 138 | env->write_records.emplace_back(8, addr, *data); |
| 135 | if (env->mutable_memory) | 139 | if (env->mutable_memory) |
| 136 | env->SetMemory8(addr, *data); | 140 | env->SetMemory8(addr, *data); |
diff --git a/src/tests/core/arm/arm_test_common.h b/src/tests/core/arm/arm_test_common.h index e4b6df194..5de8dab4e 100644 --- a/src/tests/core/arm/arm_test_common.h +++ b/src/tests/core/arm/arm_test_common.h | |||
| @@ -19,8 +19,8 @@ struct PageTable; | |||
| 19 | namespace ArmTests { | 19 | namespace ArmTests { |
| 20 | 20 | ||
| 21 | struct WriteRecord { | 21 | struct WriteRecord { |
| 22 | WriteRecord(size_t size, VAddr addr, u64 data) : size(size), addr(addr), data(data) {} | 22 | WriteRecord(std::size_t size, VAddr addr, u64 data) : size(size), addr(addr), data(data) {} |
| 23 | size_t size; | 23 | std::size_t size; |
| 24 | VAddr addr; | 24 | VAddr addr; |
| 25 | u64 data; | 25 | u64 data; |
| 26 | bool operator==(const WriteRecord& o) const { | 26 | bool operator==(const WriteRecord& o) const { |
| @@ -71,14 +71,14 @@ private: | |||
| 71 | boost::optional<u32> Read32(VAddr addr) override; | 71 | boost::optional<u32> Read32(VAddr addr) override; |
| 72 | boost::optional<u64> Read64(VAddr addr) override; | 72 | boost::optional<u64> Read64(VAddr addr) override; |
| 73 | 73 | ||
| 74 | bool ReadBlock(VAddr src_addr, void* dest_buffer, size_t size) override; | 74 | bool ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size) override; |
| 75 | 75 | ||
| 76 | bool Write8(VAddr addr, u8 data) override; | 76 | bool Write8(VAddr addr, u8 data) override; |
| 77 | bool Write16(VAddr addr, u16 data) override; | 77 | bool Write16(VAddr addr, u16 data) override; |
| 78 | bool Write32(VAddr addr, u32 data) override; | 78 | bool Write32(VAddr addr, u32 data) override; |
| 79 | bool Write64(VAddr addr, u64 data) override; | 79 | bool Write64(VAddr addr, u64 data) override; |
| 80 | 80 | ||
| 81 | bool WriteBlock(VAddr dest_addr, const void* src_buffer, size_t size) override; | 81 | bool WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size) override; |
| 82 | 82 | ||
| 83 | std::unordered_map<VAddr, u8> data; | 83 | std::unordered_map<VAddr, u8> data; |
| 84 | }; | 84 | }; |
diff --git a/src/tests/glad.cpp b/src/tests/glad.cpp deleted file mode 100644 index 1797c0e3d..000000000 --- a/src/tests/glad.cpp +++ /dev/null | |||
| @@ -1,14 +0,0 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <catch2/catch.hpp> | ||
| 6 | #include <glad/glad.h> | ||
| 7 | |||
| 8 | // This is not an actual test, but a work-around for issue #2183. | ||
| 9 | // If tests uses functions in core but doesn't explicitly use functions in glad, the linker of macOS | ||
| 10 | // will error about undefined references from video_core to glad. So we explicitly use a glad | ||
| 11 | // function here to shut up the linker. | ||
| 12 | TEST_CASE("glad fake test", "[dummy]") { | ||
| 13 | REQUIRE(&gladLoadGL != nullptr); | ||
| 14 | } | ||
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 65b5f57c3..f5ae57039 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt | |||
| @@ -5,6 +5,8 @@ add_library(video_core STATIC | |||
| 5 | debug_utils/debug_utils.h | 5 | debug_utils/debug_utils.h |
| 6 | engines/fermi_2d.cpp | 6 | engines/fermi_2d.cpp |
| 7 | engines/fermi_2d.h | 7 | engines/fermi_2d.h |
| 8 | engines/kepler_memory.cpp | ||
| 9 | engines/kepler_memory.h | ||
| 8 | engines/maxwell_3d.cpp | 10 | engines/maxwell_3d.cpp |
| 9 | engines/maxwell_3d.h | 11 | engines/maxwell_3d.h |
| 10 | engines/maxwell_compute.cpp | 12 | engines/maxwell_compute.cpp |
| @@ -12,6 +14,7 @@ add_library(video_core STATIC | |||
| 12 | engines/maxwell_dma.cpp | 14 | engines/maxwell_dma.cpp |
| 13 | engines/maxwell_dma.h | 15 | engines/maxwell_dma.h |
| 14 | engines/shader_bytecode.h | 16 | engines/shader_bytecode.h |
| 17 | engines/shader_header.h | ||
| 15 | gpu.cpp | 18 | gpu.cpp |
| 16 | gpu.h | 19 | gpu.h |
| 17 | macro_interpreter.cpp | 20 | macro_interpreter.cpp |
diff --git a/src/video_core/command_processor.cpp b/src/video_core/command_processor.cpp index 2625ddfdc..f1aa6091b 100644 --- a/src/video_core/command_processor.cpp +++ b/src/video_core/command_processor.cpp | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #include "core/tracer/recorder.h" | 14 | #include "core/tracer/recorder.h" |
| 15 | #include "video_core/command_processor.h" | 15 | #include "video_core/command_processor.h" |
| 16 | #include "video_core/engines/fermi_2d.h" | 16 | #include "video_core/engines/fermi_2d.h" |
| 17 | #include "video_core/engines/kepler_memory.h" | ||
| 17 | #include "video_core/engines/maxwell_3d.h" | 18 | #include "video_core/engines/maxwell_3d.h" |
| 18 | #include "video_core/engines/maxwell_compute.h" | 19 | #include "video_core/engines/maxwell_compute.h" |
| 19 | #include "video_core/engines/maxwell_dma.h" | 20 | #include "video_core/engines/maxwell_dma.h" |
| @@ -69,6 +70,9 @@ void GPU::ProcessCommandLists(const std::vector<CommandListHeader>& commands) { | |||
| 69 | case EngineID::MAXWELL_DMA_COPY_A: | 70 | case EngineID::MAXWELL_DMA_COPY_A: |
| 70 | maxwell_dma->WriteReg(method, value); | 71 | maxwell_dma->WriteReg(method, value); |
| 71 | break; | 72 | break; |
| 73 | case EngineID::KEPLER_INLINE_TO_MEMORY_B: | ||
| 74 | kepler_memory->WriteReg(method, value); | ||
| 75 | break; | ||
| 72 | default: | 76 | default: |
| 73 | UNIMPLEMENTED_MSG("Unimplemented engine"); | 77 | UNIMPLEMENTED_MSG("Unimplemented engine"); |
| 74 | } | 78 | } |
diff --git a/src/video_core/engines/fermi_2d.h b/src/video_core/engines/fermi_2d.h index dcf9ef8b9..021b83eaa 100644 --- a/src/video_core/engines/fermi_2d.h +++ b/src/video_core/engines/fermi_2d.h | |||
| @@ -26,7 +26,7 @@ public: | |||
| 26 | void WriteReg(u32 method, u32 value); | 26 | void WriteReg(u32 method, u32 value); |
| 27 | 27 | ||
| 28 | struct Regs { | 28 | struct Regs { |
| 29 | static constexpr size_t NUM_REGS = 0x258; | 29 | static constexpr std::size_t NUM_REGS = 0x258; |
| 30 | 30 | ||
| 31 | struct Surface { | 31 | struct Surface { |
| 32 | RenderTargetFormat format; | 32 | RenderTargetFormat format; |
diff --git a/src/video_core/engines/kepler_memory.cpp b/src/video_core/engines/kepler_memory.cpp new file mode 100644 index 000000000..66ae6332d --- /dev/null +++ b/src/video_core/engines/kepler_memory.cpp | |||
| @@ -0,0 +1,45 @@ | |||
| 1 | // Copyright 2018 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/logging/log.h" | ||
| 6 | #include "core/memory.h" | ||
| 7 | #include "video_core/engines/kepler_memory.h" | ||
| 8 | |||
| 9 | namespace Tegra::Engines { | ||
| 10 | |||
| 11 | KeplerMemory::KeplerMemory(MemoryManager& memory_manager) : memory_manager(memory_manager) {} | ||
| 12 | KeplerMemory::~KeplerMemory() = default; | ||
| 13 | |||
| 14 | void KeplerMemory::WriteReg(u32 method, u32 value) { | ||
| 15 | ASSERT_MSG(method < Regs::NUM_REGS, | ||
| 16 | "Invalid KeplerMemory register, increase the size of the Regs structure"); | ||
| 17 | |||
| 18 | regs.reg_array[method] = value; | ||
| 19 | |||
| 20 | switch (method) { | ||
| 21 | case KEPLERMEMORY_REG_INDEX(exec): { | ||
| 22 | state.write_offset = 0; | ||
| 23 | break; | ||
| 24 | } | ||
| 25 | case KEPLERMEMORY_REG_INDEX(data): { | ||
| 26 | ProcessData(value); | ||
| 27 | break; | ||
| 28 | } | ||
| 29 | } | ||
| 30 | } | ||
| 31 | |||
| 32 | void KeplerMemory::ProcessData(u32 data) { | ||
| 33 | ASSERT_MSG(regs.exec.linear, "Non-linear uploads are not supported"); | ||
| 34 | ASSERT(regs.dest.x == 0 && regs.dest.y == 0 && regs.dest.z == 0); | ||
| 35 | |||
| 36 | GPUVAddr address = regs.dest.Address(); | ||
| 37 | VAddr dest_address = | ||
| 38 | *memory_manager.GpuToCpuAddress(address + state.write_offset * sizeof(u32)); | ||
| 39 | |||
| 40 | Memory::Write32(dest_address, data); | ||
| 41 | |||
| 42 | state.write_offset++; | ||
| 43 | } | ||
| 44 | |||
| 45 | } // namespace Tegra::Engines | ||
diff --git a/src/video_core/engines/kepler_memory.h b/src/video_core/engines/kepler_memory.h new file mode 100644 index 000000000..b0d0078cf --- /dev/null +++ b/src/video_core/engines/kepler_memory.h | |||
| @@ -0,0 +1,90 @@ | |||
| 1 | // Copyright 2018 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include "common/assert.h" | ||
| 9 | #include "common/bit_field.h" | ||
| 10 | #include "common/common_funcs.h" | ||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "video_core/memory_manager.h" | ||
| 13 | |||
| 14 | namespace Tegra::Engines { | ||
| 15 | |||
| 16 | #define KEPLERMEMORY_REG_INDEX(field_name) \ | ||
| 17 | (offsetof(Tegra::Engines::KeplerMemory::Regs, field_name) / sizeof(u32)) | ||
| 18 | |||
| 19 | class KeplerMemory final { | ||
| 20 | public: | ||
| 21 | KeplerMemory(MemoryManager& memory_manager); | ||
| 22 | ~KeplerMemory(); | ||
| 23 | |||
| 24 | /// Write the value to the register identified by method. | ||
| 25 | void WriteReg(u32 method, u32 value); | ||
| 26 | |||
| 27 | struct Regs { | ||
| 28 | static constexpr size_t NUM_REGS = 0x7F; | ||
| 29 | |||
| 30 | union { | ||
| 31 | struct { | ||
| 32 | INSERT_PADDING_WORDS(0x60); | ||
| 33 | |||
| 34 | u32 line_length_in; | ||
| 35 | u32 line_count; | ||
| 36 | |||
| 37 | struct { | ||
| 38 | u32 address_high; | ||
| 39 | u32 address_low; | ||
| 40 | u32 pitch; | ||
| 41 | u32 block_dimensions; | ||
| 42 | u32 width; | ||
| 43 | u32 height; | ||
| 44 | u32 depth; | ||
| 45 | u32 z; | ||
| 46 | u32 x; | ||
| 47 | u32 y; | ||
| 48 | |||
| 49 | GPUVAddr Address() const { | ||
| 50 | return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) | | ||
| 51 | address_low); | ||
| 52 | } | ||
| 53 | } dest; | ||
| 54 | |||
| 55 | struct { | ||
| 56 | union { | ||
| 57 | BitField<0, 1, u32> linear; | ||
| 58 | }; | ||
| 59 | } exec; | ||
| 60 | |||
| 61 | u32 data; | ||
| 62 | |||
| 63 | INSERT_PADDING_WORDS(0x11); | ||
| 64 | }; | ||
| 65 | std::array<u32, NUM_REGS> reg_array; | ||
| 66 | }; | ||
| 67 | } regs{}; | ||
| 68 | |||
| 69 | struct { | ||
| 70 | u32 write_offset = 0; | ||
| 71 | } state{}; | ||
| 72 | |||
| 73 | private: | ||
| 74 | MemoryManager& memory_manager; | ||
| 75 | |||
| 76 | void ProcessData(u32 data); | ||
| 77 | }; | ||
| 78 | |||
| 79 | #define ASSERT_REG_POSITION(field_name, position) \ | ||
| 80 | static_assert(offsetof(KeplerMemory::Regs, field_name) == position * 4, \ | ||
| 81 | "Field " #field_name " has invalid position") | ||
| 82 | |||
| 83 | ASSERT_REG_POSITION(line_length_in, 0x60); | ||
| 84 | ASSERT_REG_POSITION(line_count, 0x61); | ||
| 85 | ASSERT_REG_POSITION(dest, 0x62); | ||
| 86 | ASSERT_REG_POSITION(exec, 0x6C); | ||
| 87 | ASSERT_REG_POSITION(data, 0x6D); | ||
| 88 | #undef ASSERT_REG_POSITION | ||
| 89 | |||
| 90 | } // namespace Tegra::Engines | ||
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 329079ddd..8afd26fe9 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp | |||
| @@ -248,8 +248,8 @@ void Maxwell3D::DrawArrays() { | |||
| 248 | 248 | ||
| 249 | void Maxwell3D::ProcessCBBind(Regs::ShaderStage stage) { | 249 | void Maxwell3D::ProcessCBBind(Regs::ShaderStage stage) { |
| 250 | // Bind the buffer currently in CB_ADDRESS to the specified index in the desired shader stage. | 250 | // Bind the buffer currently in CB_ADDRESS to the specified index in the desired shader stage. |
| 251 | auto& shader = state.shader_stages[static_cast<size_t>(stage)]; | 251 | auto& shader = state.shader_stages[static_cast<std::size_t>(stage)]; |
| 252 | auto& bind_data = regs.cb_bind[static_cast<size_t>(stage)]; | 252 | auto& bind_data = regs.cb_bind[static_cast<std::size_t>(stage)]; |
| 253 | 253 | ||
| 254 | auto& buffer = shader.const_buffers[bind_data.index]; | 254 | auto& buffer = shader.const_buffers[bind_data.index]; |
| 255 | 255 | ||
| @@ -316,14 +316,14 @@ Texture::TSCEntry Maxwell3D::GetTSCEntry(u32 tsc_index) const { | |||
| 316 | std::vector<Texture::FullTextureInfo> Maxwell3D::GetStageTextures(Regs::ShaderStage stage) const { | 316 | std::vector<Texture::FullTextureInfo> Maxwell3D::GetStageTextures(Regs::ShaderStage stage) const { |
| 317 | std::vector<Texture::FullTextureInfo> textures; | 317 | std::vector<Texture::FullTextureInfo> textures; |
| 318 | 318 | ||
| 319 | auto& fragment_shader = state.shader_stages[static_cast<size_t>(stage)]; | 319 | auto& fragment_shader = state.shader_stages[static_cast<std::size_t>(stage)]; |
| 320 | auto& tex_info_buffer = fragment_shader.const_buffers[regs.tex_cb_index]; | 320 | auto& tex_info_buffer = fragment_shader.const_buffers[regs.tex_cb_index]; |
| 321 | ASSERT(tex_info_buffer.enabled && tex_info_buffer.address != 0); | 321 | ASSERT(tex_info_buffer.enabled && tex_info_buffer.address != 0); |
| 322 | 322 | ||
| 323 | GPUVAddr tex_info_buffer_end = tex_info_buffer.address + tex_info_buffer.size; | 323 | GPUVAddr tex_info_buffer_end = tex_info_buffer.address + tex_info_buffer.size; |
| 324 | 324 | ||
| 325 | // Offset into the texture constbuffer where the texture info begins. | 325 | // Offset into the texture constbuffer where the texture info begins. |
| 326 | static constexpr size_t TextureInfoOffset = 0x20; | 326 | static constexpr std::size_t TextureInfoOffset = 0x20; |
| 327 | 327 | ||
| 328 | for (GPUVAddr current_texture = tex_info_buffer.address + TextureInfoOffset; | 328 | for (GPUVAddr current_texture = tex_info_buffer.address + TextureInfoOffset; |
| 329 | current_texture < tex_info_buffer_end; current_texture += sizeof(Texture::TextureHandle)) { | 329 | current_texture < tex_info_buffer_end; current_texture += sizeof(Texture::TextureHandle)) { |
| @@ -360,8 +360,9 @@ std::vector<Texture::FullTextureInfo> Maxwell3D::GetStageTextures(Regs::ShaderSt | |||
| 360 | return textures; | 360 | return textures; |
| 361 | } | 361 | } |
| 362 | 362 | ||
| 363 | Texture::FullTextureInfo Maxwell3D::GetStageTexture(Regs::ShaderStage stage, size_t offset) const { | 363 | Texture::FullTextureInfo Maxwell3D::GetStageTexture(Regs::ShaderStage stage, |
| 364 | auto& shader = state.shader_stages[static_cast<size_t>(stage)]; | 364 | std::size_t offset) const { |
| 365 | auto& shader = state.shader_stages[static_cast<std::size_t>(stage)]; | ||
| 365 | auto& tex_info_buffer = shader.const_buffers[regs.tex_cb_index]; | 366 | auto& tex_info_buffer = shader.const_buffers[regs.tex_cb_index]; |
| 366 | ASSERT(tex_info_buffer.enabled && tex_info_buffer.address != 0); | 367 | ASSERT(tex_info_buffer.enabled && tex_info_buffer.address != 0); |
| 367 | 368 | ||
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index d3be900a4..9f5581045 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h | |||
| @@ -34,17 +34,18 @@ public: | |||
| 34 | /// Register structure of the Maxwell3D engine. | 34 | /// Register structure of the Maxwell3D engine. |
| 35 | /// TODO(Subv): This structure will need to be made bigger as more registers are discovered. | 35 | /// TODO(Subv): This structure will need to be made bigger as more registers are discovered. |
| 36 | struct Regs { | 36 | struct Regs { |
| 37 | static constexpr size_t NUM_REGS = 0xE00; | 37 | static constexpr std::size_t NUM_REGS = 0xE00; |
| 38 | 38 | ||
| 39 | static constexpr size_t NumRenderTargets = 8; | 39 | static constexpr std::size_t NumRenderTargets = 8; |
| 40 | static constexpr size_t NumViewports = 16; | 40 | static constexpr std::size_t NumViewports = 16; |
| 41 | static constexpr size_t NumCBData = 16; | 41 | static constexpr std::size_t NumCBData = 16; |
| 42 | static constexpr size_t NumVertexArrays = 32; | 42 | static constexpr std::size_t NumVertexArrays = 32; |
| 43 | static constexpr size_t NumVertexAttributes = 32; | 43 | static constexpr std::size_t NumVertexAttributes = 32; |
| 44 | static constexpr size_t MaxShaderProgram = 6; | 44 | static constexpr std::size_t NumTextureSamplers = 32; |
| 45 | static constexpr size_t MaxShaderStage = 5; | 45 | static constexpr std::size_t MaxShaderProgram = 6; |
| 46 | static constexpr std::size_t MaxShaderStage = 5; | ||
| 46 | // Maximum number of const buffers per shader stage. | 47 | // Maximum number of const buffers per shader stage. |
| 47 | static constexpr size_t MaxConstBuffers = 18; | 48 | static constexpr std::size_t MaxConstBuffers = 18; |
| 48 | 49 | ||
| 49 | enum class QueryMode : u32 { | 50 | enum class QueryMode : u32 { |
| 50 | Write = 0, | 51 | Write = 0, |
| @@ -443,9 +444,9 @@ public: | |||
| 443 | } | 444 | } |
| 444 | }; | 445 | }; |
| 445 | 446 | ||
| 446 | bool IsShaderConfigEnabled(size_t index) const { | 447 | bool IsShaderConfigEnabled(std::size_t index) const { |
| 447 | // The VertexB is always enabled. | 448 | // The VertexB is always enabled. |
| 448 | if (index == static_cast<size_t>(Regs::ShaderProgram::VertexB)) { | 449 | if (index == static_cast<std::size_t>(Regs::ShaderProgram::VertexB)) { |
| 449 | return true; | 450 | return true; |
| 450 | } | 451 | } |
| 451 | return shader_config[index].enable != 0; | 452 | return shader_config[index].enable != 0; |
| @@ -461,7 +462,11 @@ public: | |||
| 461 | u32 entry; | 462 | u32 entry; |
| 462 | } macros; | 463 | } macros; |
| 463 | 464 | ||
| 464 | INSERT_PADDING_WORDS(0x1B8); | 465 | INSERT_PADDING_WORDS(0x189); |
| 466 | |||
| 467 | u32 tfb_enabled; | ||
| 468 | |||
| 469 | INSERT_PADDING_WORDS(0x2E); | ||
| 465 | 470 | ||
| 466 | RenderTargetConfig rt[NumRenderTargets]; | 471 | RenderTargetConfig rt[NumRenderTargets]; |
| 467 | 472 | ||
| @@ -571,7 +576,7 @@ public: | |||
| 571 | BitField<25, 3, u32> map_7; | 576 | BitField<25, 3, u32> map_7; |
| 572 | }; | 577 | }; |
| 573 | 578 | ||
| 574 | u32 GetMap(size_t index) const { | 579 | u32 GetMap(std::size_t index) const { |
| 575 | const std::array<u32, NumRenderTargets> maps{map_0, map_1, map_2, map_3, | 580 | const std::array<u32, NumRenderTargets> maps{map_0, map_1, map_2, map_3, |
| 576 | map_4, map_5, map_6, map_7}; | 581 | map_4, map_5, map_6, map_7}; |
| 577 | ASSERT(index < maps.size()); | 582 | ASSERT(index < maps.size()); |
| @@ -594,7 +599,9 @@ public: | |||
| 594 | 599 | ||
| 595 | u32 depth_write_enabled; | 600 | u32 depth_write_enabled; |
| 596 | 601 | ||
| 597 | INSERT_PADDING_WORDS(0x7); | 602 | u32 alpha_test_enabled; |
| 603 | |||
| 604 | INSERT_PADDING_WORDS(0x6); | ||
| 598 | 605 | ||
| 599 | u32 d3d_cull_mode; | 606 | u32 d3d_cull_mode; |
| 600 | 607 | ||
| @@ -635,7 +642,11 @@ public: | |||
| 635 | 642 | ||
| 636 | u32 vb_element_base; | 643 | u32 vb_element_base; |
| 637 | 644 | ||
| 638 | INSERT_PADDING_WORDS(0x40); | 645 | INSERT_PADDING_WORDS(0x38); |
| 646 | |||
| 647 | float point_size; | ||
| 648 | |||
| 649 | INSERT_PADDING_WORDS(0x7); | ||
| 639 | 650 | ||
| 640 | u32 zeta_enable; | 651 | u32 zeta_enable; |
| 641 | 652 | ||
| @@ -925,7 +936,7 @@ public: | |||
| 925 | std::vector<Texture::FullTextureInfo> GetStageTextures(Regs::ShaderStage stage) const; | 936 | std::vector<Texture::FullTextureInfo> GetStageTextures(Regs::ShaderStage stage) const; |
| 926 | 937 | ||
| 927 | /// Returns the texture information for a specific texture in a specific shader stage. | 938 | /// Returns the texture information for a specific texture in a specific shader stage. |
| 928 | Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, size_t offset) const; | 939 | Texture::FullTextureInfo GetStageTexture(Regs::ShaderStage stage, std::size_t offset) const; |
| 929 | 940 | ||
| 930 | private: | 941 | private: |
| 931 | VideoCore::RasterizerInterface& rasterizer; | 942 | VideoCore::RasterizerInterface& rasterizer; |
| @@ -977,6 +988,7 @@ private: | |||
| 977 | "Field " #field_name " has invalid position") | 988 | "Field " #field_name " has invalid position") |
| 978 | 989 | ||
| 979 | ASSERT_REG_POSITION(macros, 0x45); | 990 | ASSERT_REG_POSITION(macros, 0x45); |
| 991 | ASSERT_REG_POSITION(tfb_enabled, 0x1D1); | ||
| 980 | ASSERT_REG_POSITION(rt, 0x200); | 992 | ASSERT_REG_POSITION(rt, 0x200); |
| 981 | ASSERT_REG_POSITION(viewport_transform[0], 0x280); | 993 | ASSERT_REG_POSITION(viewport_transform[0], 0x280); |
| 982 | ASSERT_REG_POSITION(viewport, 0x300); | 994 | ASSERT_REG_POSITION(viewport, 0x300); |
| @@ -996,6 +1008,7 @@ ASSERT_REG_POSITION(zeta_height, 0x48b); | |||
| 996 | ASSERT_REG_POSITION(depth_test_enable, 0x4B3); | 1008 | ASSERT_REG_POSITION(depth_test_enable, 0x4B3); |
| 997 | ASSERT_REG_POSITION(independent_blend_enable, 0x4B9); | 1009 | ASSERT_REG_POSITION(independent_blend_enable, 0x4B9); |
| 998 | ASSERT_REG_POSITION(depth_write_enabled, 0x4BA); | 1010 | ASSERT_REG_POSITION(depth_write_enabled, 0x4BA); |
| 1011 | ASSERT_REG_POSITION(alpha_test_enabled, 0x4BB); | ||
| 999 | ASSERT_REG_POSITION(d3d_cull_mode, 0x4C2); | 1012 | ASSERT_REG_POSITION(d3d_cull_mode, 0x4C2); |
| 1000 | ASSERT_REG_POSITION(depth_test_func, 0x4C3); | 1013 | ASSERT_REG_POSITION(depth_test_func, 0x4C3); |
| 1001 | ASSERT_REG_POSITION(blend, 0x4CF); | 1014 | ASSERT_REG_POSITION(blend, 0x4CF); |
| @@ -1009,6 +1022,7 @@ ASSERT_REG_POSITION(stencil_front_func_mask, 0x4E6); | |||
| 1009 | ASSERT_REG_POSITION(stencil_front_mask, 0x4E7); | 1022 | ASSERT_REG_POSITION(stencil_front_mask, 0x4E7); |
| 1010 | ASSERT_REG_POSITION(screen_y_control, 0x4EB); | 1023 | ASSERT_REG_POSITION(screen_y_control, 0x4EB); |
| 1011 | ASSERT_REG_POSITION(vb_element_base, 0x50D); | 1024 | ASSERT_REG_POSITION(vb_element_base, 0x50D); |
| 1025 | ASSERT_REG_POSITION(point_size, 0x546); | ||
| 1012 | ASSERT_REG_POSITION(zeta_enable, 0x54E); | 1026 | ASSERT_REG_POSITION(zeta_enable, 0x54E); |
| 1013 | ASSERT_REG_POSITION(tsc, 0x557); | 1027 | ASSERT_REG_POSITION(tsc, 0x557); |
| 1014 | ASSERT_REG_POSITION(tic, 0x55D); | 1028 | ASSERT_REG_POSITION(tic, 0x55D); |
diff --git a/src/video_core/engines/maxwell_compute.cpp b/src/video_core/engines/maxwell_compute.cpp index e4e5f9e5e..59e28b22d 100644 --- a/src/video_core/engines/maxwell_compute.cpp +++ b/src/video_core/engines/maxwell_compute.cpp | |||
| @@ -2,12 +2,29 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/logging/log.h" | ||
| 6 | #include "core/core.h" | ||
| 5 | #include "video_core/engines/maxwell_compute.h" | 7 | #include "video_core/engines/maxwell_compute.h" |
| 6 | 8 | ||
| 7 | namespace Tegra { | 9 | namespace Tegra { |
| 8 | namespace Engines { | 10 | namespace Engines { |
| 9 | 11 | ||
| 10 | void MaxwellCompute::WriteReg(u32 method, u32 value) {} | 12 | void MaxwellCompute::WriteReg(u32 method, u32 value) { |
| 13 | ASSERT_MSG(method < Regs::NUM_REGS, | ||
| 14 | "Invalid MaxwellCompute register, increase the size of the Regs structure"); | ||
| 15 | |||
| 16 | regs.reg_array[method] = value; | ||
| 17 | |||
| 18 | switch (method) { | ||
| 19 | case MAXWELL_COMPUTE_REG_INDEX(compute): { | ||
| 20 | LOG_CRITICAL(HW_GPU, "Compute shaders are not implemented"); | ||
| 21 | UNREACHABLE(); | ||
| 22 | break; | ||
| 23 | } | ||
| 24 | default: | ||
| 25 | break; | ||
| 26 | } | ||
| 27 | } | ||
| 11 | 28 | ||
| 12 | } // namespace Engines | 29 | } // namespace Engines |
| 13 | } // namespace Tegra | 30 | } // namespace Tegra |
diff --git a/src/video_core/engines/maxwell_compute.h b/src/video_core/engines/maxwell_compute.h index 2b3e4ced6..6ea934fb9 100644 --- a/src/video_core/engines/maxwell_compute.h +++ b/src/video_core/engines/maxwell_compute.h | |||
| @@ -4,17 +4,53 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | ||
| 8 | #include "common/assert.h" | ||
| 9 | #include "common/bit_field.h" | ||
| 10 | #include "common/common_funcs.h" | ||
| 7 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 8 | 12 | ||
| 9 | namespace Tegra::Engines { | 13 | namespace Tegra::Engines { |
| 10 | 14 | ||
| 15 | #define MAXWELL_COMPUTE_REG_INDEX(field_name) \ | ||
| 16 | (offsetof(Tegra::Engines::MaxwellCompute::Regs, field_name) / sizeof(u32)) | ||
| 17 | |||
| 11 | class MaxwellCompute final { | 18 | class MaxwellCompute final { |
| 12 | public: | 19 | public: |
| 13 | MaxwellCompute() = default; | 20 | MaxwellCompute() = default; |
| 14 | ~MaxwellCompute() = default; | 21 | ~MaxwellCompute() = default; |
| 15 | 22 | ||
| 23 | struct Regs { | ||
| 24 | static constexpr std::size_t NUM_REGS = 0xCF8; | ||
| 25 | |||
| 26 | union { | ||
| 27 | struct { | ||
| 28 | INSERT_PADDING_WORDS(0x281); | ||
| 29 | |||
| 30 | union { | ||
| 31 | u32 compute_end; | ||
| 32 | BitField<0, 1, u32> unknown; | ||
| 33 | } compute; | ||
| 34 | |||
| 35 | INSERT_PADDING_WORDS(0xA76); | ||
| 36 | }; | ||
| 37 | std::array<u32, NUM_REGS> reg_array; | ||
| 38 | }; | ||
| 39 | } regs{}; | ||
| 40 | |||
| 41 | static_assert(sizeof(Regs) == Regs::NUM_REGS * sizeof(u32), | ||
| 42 | "MaxwellCompute Regs has wrong size"); | ||
| 43 | |||
| 16 | /// Write the value to the register identified by method. | 44 | /// Write the value to the register identified by method. |
| 17 | void WriteReg(u32 method, u32 value); | 45 | void WriteReg(u32 method, u32 value); |
| 18 | }; | 46 | }; |
| 19 | 47 | ||
| 48 | #define ASSERT_REG_POSITION(field_name, position) \ | ||
| 49 | static_assert(offsetof(MaxwellCompute::Regs, field_name) == position * 4, \ | ||
| 50 | "Field " #field_name " has invalid position") | ||
| 51 | |||
| 52 | ASSERT_REG_POSITION(compute, 0x281); | ||
| 53 | |||
| 54 | #undef ASSERT_REG_POSITION | ||
| 55 | |||
| 20 | } // namespace Tegra::Engines | 56 | } // namespace Tegra::Engines |
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp index c24d33d5c..aa7481b8c 100644 --- a/src/video_core/engines/maxwell_dma.cpp +++ b/src/video_core/engines/maxwell_dma.cpp | |||
| @@ -50,7 +50,7 @@ void MaxwellDMA::HandleCopy() { | |||
| 50 | ASSERT(regs.dst_params.pos_y == 0); | 50 | ASSERT(regs.dst_params.pos_y == 0); |
| 51 | 51 | ||
| 52 | if (regs.exec.is_dst_linear == regs.exec.is_src_linear) { | 52 | if (regs.exec.is_dst_linear == regs.exec.is_src_linear) { |
| 53 | size_t copy_size = regs.x_count; | 53 | std::size_t copy_size = regs.x_count; |
| 54 | 54 | ||
| 55 | // When the enable_2d bit is disabled, the copy is performed as if we were copying a 1D | 55 | // When the enable_2d bit is disabled, the copy is performed as if we were copying a 1D |
| 56 | // buffer of length `x_count`, otherwise we copy a 2D buffer of size (x_count, y_count). | 56 | // buffer of length `x_count`, otherwise we copy a 2D buffer of size (x_count, y_count). |
diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h index 7882f16e0..311ccb616 100644 --- a/src/video_core/engines/maxwell_dma.h +++ b/src/video_core/engines/maxwell_dma.h | |||
| @@ -23,7 +23,7 @@ public: | |||
| 23 | void WriteReg(u32 method, u32 value); | 23 | void WriteReg(u32 method, u32 value); |
| 24 | 24 | ||
| 25 | struct Regs { | 25 | struct Regs { |
| 26 | static constexpr size_t NUM_REGS = 0x1D6; | 26 | static constexpr std::size_t NUM_REGS = 0x1D6; |
| 27 | 27 | ||
| 28 | struct Parameters { | 28 | struct Parameters { |
| 29 | union { | 29 | union { |
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index 58f2904ce..b1f137b9c 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h | |||
| @@ -5,9 +5,8 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <bitset> | 7 | #include <bitset> |
| 8 | #include <cstring> | ||
| 9 | #include <map> | ||
| 10 | #include <string> | 8 | #include <string> |
| 9 | #include <tuple> | ||
| 11 | #include <vector> | 10 | #include <vector> |
| 12 | 11 | ||
| 13 | #include <boost/optional.hpp> | 12 | #include <boost/optional.hpp> |
| @@ -20,10 +19,10 @@ namespace Tegra::Shader { | |||
| 20 | 19 | ||
| 21 | struct Register { | 20 | struct Register { |
| 22 | /// Number of registers | 21 | /// Number of registers |
| 23 | static constexpr size_t NumRegisters = 256; | 22 | static constexpr std::size_t NumRegisters = 256; |
| 24 | 23 | ||
| 25 | /// Register 255 is special cased to always be 0 | 24 | /// Register 255 is special cased to always be 0 |
| 26 | static constexpr size_t ZeroIndex = 255; | 25 | static constexpr std::size_t ZeroIndex = 255; |
| 27 | 26 | ||
| 28 | enum class Size : u64 { | 27 | enum class Size : u64 { |
| 29 | Byte = 0, | 28 | Byte = 0, |
| @@ -67,6 +66,13 @@ private: | |||
| 67 | u64 value{}; | 66 | u64 value{}; |
| 68 | }; | 67 | }; |
| 69 | 68 | ||
| 69 | enum class AttributeSize : u64 { | ||
| 70 | Word = 0, | ||
| 71 | DoubleWord = 1, | ||
| 72 | TripleWord = 2, | ||
| 73 | QuadWord = 3, | ||
| 74 | }; | ||
| 75 | |||
| 70 | union Attribute { | 76 | union Attribute { |
| 71 | Attribute() = default; | 77 | Attribute() = default; |
| 72 | 78 | ||
| @@ -87,9 +93,10 @@ union Attribute { | |||
| 87 | }; | 93 | }; |
| 88 | 94 | ||
| 89 | union { | 95 | union { |
| 96 | BitField<20, 10, u64> immediate; | ||
| 90 | BitField<22, 2, u64> element; | 97 | BitField<22, 2, u64> element; |
| 91 | BitField<24, 6, Index> index; | 98 | BitField<24, 6, Index> index; |
| 92 | BitField<47, 3, u64> size; | 99 | BitField<47, 3, AttributeSize> size; |
| 93 | } fmt20; | 100 | } fmt20; |
| 94 | 101 | ||
| 95 | union { | 102 | union { |
| @@ -232,6 +239,41 @@ enum class FlowCondition : u64 { | |||
| 232 | Fcsm_Tr = 0x1C, // TODO(bunnei): What is this used for? | 239 | Fcsm_Tr = 0x1C, // TODO(bunnei): What is this used for? |
| 233 | }; | 240 | }; |
| 234 | 241 | ||
| 242 | enum class ControlCode : u64 { | ||
| 243 | F = 0, | ||
| 244 | LT = 1, | ||
| 245 | EQ = 2, | ||
| 246 | LE = 3, | ||
| 247 | GT = 4, | ||
| 248 | NE = 5, | ||
| 249 | GE = 6, | ||
| 250 | Num = 7, | ||
| 251 | Nan = 8, | ||
| 252 | LTU = 9, | ||
| 253 | EQU = 10, | ||
| 254 | LEU = 11, | ||
| 255 | GTU = 12, | ||
| 256 | NEU = 13, | ||
| 257 | GEU = 14, | ||
| 258 | // | ||
| 259 | OFF = 16, | ||
| 260 | LO = 17, | ||
| 261 | SFF = 18, | ||
| 262 | LS = 19, | ||
| 263 | HI = 20, | ||
| 264 | SFT = 21, | ||
| 265 | HS = 22, | ||
| 266 | OFT = 23, | ||
| 267 | CSM_TA = 24, | ||
| 268 | CSM_TR = 25, | ||
| 269 | CSM_MX = 26, | ||
| 270 | FCSM_TA = 27, | ||
| 271 | FCSM_TR = 28, | ||
| 272 | FCSM_MX = 29, | ||
| 273 | RLE = 30, | ||
| 274 | RGT = 31, | ||
| 275 | }; | ||
| 276 | |||
| 235 | enum class PredicateResultMode : u64 { | 277 | enum class PredicateResultMode : u64 { |
| 236 | None = 0x0, | 278 | None = 0x0, |
| 237 | NotZero = 0x3, | 279 | NotZero = 0x3, |
| @@ -263,17 +305,38 @@ enum class TextureProcessMode : u64 { | |||
| 263 | LLA = 7 // Load LOD. The A is unknown, does not appear to differ with LL | 305 | LLA = 7 // Load LOD. The A is unknown, does not appear to differ with LL |
| 264 | }; | 306 | }; |
| 265 | 307 | ||
| 266 | enum class IpaInterpMode : u64 { Linear = 0, Perspective = 1, Flat = 2, Sc = 3 }; | 308 | enum class TextureMiscMode : u64 { |
| 267 | enum class IpaSampleMode : u64 { Default = 0, Centroid = 1, Offset = 2 }; | 309 | DC, |
| 310 | AOFFI, // Uses Offset | ||
| 311 | NDV, | ||
| 312 | NODEP, | ||
| 313 | MZ, | ||
| 314 | PTP, | ||
| 315 | }; | ||
| 316 | |||
| 317 | enum class IpaInterpMode : u64 { | ||
| 318 | Linear = 0, | ||
| 319 | Perspective = 1, | ||
| 320 | Flat = 2, | ||
| 321 | Sc = 3, | ||
| 322 | }; | ||
| 323 | |||
| 324 | enum class IpaSampleMode : u64 { | ||
| 325 | Default = 0, | ||
| 326 | Centroid = 1, | ||
| 327 | Offset = 2, | ||
| 328 | }; | ||
| 268 | 329 | ||
| 269 | struct IpaMode { | 330 | struct IpaMode { |
| 270 | IpaInterpMode interpolation_mode; | 331 | IpaInterpMode interpolation_mode; |
| 271 | IpaSampleMode sampling_mode; | 332 | IpaSampleMode sampling_mode; |
| 272 | inline bool operator==(const IpaMode& a) { | 333 | |
| 273 | return (a.interpolation_mode == interpolation_mode) && (a.sampling_mode == sampling_mode); | 334 | bool operator==(const IpaMode& a) const { |
| 335 | return std::tie(interpolation_mode, sampling_mode) == | ||
| 336 | std::tie(a.interpolation_mode, a.sampling_mode); | ||
| 274 | } | 337 | } |
| 275 | inline bool operator!=(const IpaMode& a) { | 338 | bool operator!=(const IpaMode& a) const { |
| 276 | return !((*this) == a); | 339 | return !operator==(a); |
| 277 | } | 340 | } |
| 278 | }; | 341 | }; |
| 279 | 342 | ||
| @@ -538,6 +601,15 @@ union Instruction { | |||
| 538 | } pset; | 601 | } pset; |
| 539 | 602 | ||
| 540 | union { | 603 | union { |
| 604 | BitField<0, 3, u64> pred0; | ||
| 605 | BitField<3, 3, u64> pred3; | ||
| 606 | BitField<8, 5, ControlCode> cc; // flag in cc | ||
| 607 | BitField<39, 3, u64> pred39; | ||
| 608 | BitField<42, 1, u64> neg_pred39; | ||
| 609 | BitField<45, 4, PredOperation> op; // op with pred39 | ||
| 610 | } csetp; | ||
| 611 | |||
| 612 | union { | ||
| 541 | BitField<39, 3, u64> pred39; | 613 | BitField<39, 3, u64> pred39; |
| 542 | BitField<42, 1, u64> neg_pred; | 614 | BitField<42, 1, u64> neg_pred; |
| 543 | BitField<43, 1, u64> neg_a; | 615 | BitField<43, 1, u64> neg_a; |
| @@ -582,42 +654,127 @@ union Instruction { | |||
| 582 | BitField<28, 1, u64> array; | 654 | BitField<28, 1, u64> array; |
| 583 | BitField<29, 2, TextureType> texture_type; | 655 | BitField<29, 2, TextureType> texture_type; |
| 584 | BitField<31, 4, u64> component_mask; | 656 | BitField<31, 4, u64> component_mask; |
| 657 | BitField<49, 1, u64> nodep_flag; | ||
| 658 | BitField<50, 1, u64> dc_flag; | ||
| 659 | BitField<54, 1, u64> aoffi_flag; | ||
| 585 | BitField<55, 3, TextureProcessMode> process_mode; | 660 | BitField<55, 3, TextureProcessMode> process_mode; |
| 586 | 661 | ||
| 587 | bool IsComponentEnabled(size_t component) const { | 662 | bool IsComponentEnabled(std::size_t component) const { |
| 588 | return ((1ull << component) & component_mask) != 0; | 663 | return ((1ull << component) & component_mask) != 0; |
| 589 | } | 664 | } |
| 665 | |||
| 666 | TextureProcessMode GetTextureProcessMode() const { | ||
| 667 | return process_mode; | ||
| 668 | } | ||
| 669 | |||
| 670 | bool UsesMiscMode(TextureMiscMode mode) const { | ||
| 671 | switch (mode) { | ||
| 672 | case TextureMiscMode::DC: | ||
| 673 | return dc_flag != 0; | ||
| 674 | case TextureMiscMode::NODEP: | ||
| 675 | return nodep_flag != 0; | ||
| 676 | case TextureMiscMode::AOFFI: | ||
| 677 | return aoffi_flag != 0; | ||
| 678 | default: | ||
| 679 | break; | ||
| 680 | } | ||
| 681 | return false; | ||
| 682 | } | ||
| 590 | } tex; | 683 | } tex; |
| 591 | 684 | ||
| 592 | union { | 685 | union { |
| 593 | BitField<22, 6, TextureQueryType> query_type; | 686 | BitField<22, 6, TextureQueryType> query_type; |
| 594 | BitField<31, 4, u64> component_mask; | 687 | BitField<31, 4, u64> component_mask; |
| 688 | BitField<49, 1, u64> nodep_flag; | ||
| 689 | |||
| 690 | bool UsesMiscMode(TextureMiscMode mode) const { | ||
| 691 | switch (mode) { | ||
| 692 | case TextureMiscMode::NODEP: | ||
| 693 | return nodep_flag != 0; | ||
| 694 | default: | ||
| 695 | break; | ||
| 696 | } | ||
| 697 | return false; | ||
| 698 | } | ||
| 595 | } txq; | 699 | } txq; |
| 596 | 700 | ||
| 597 | union { | 701 | union { |
| 598 | BitField<28, 1, u64> array; | 702 | BitField<28, 1, u64> array; |
| 599 | BitField<29, 2, TextureType> texture_type; | 703 | BitField<29, 2, TextureType> texture_type; |
| 600 | BitField<31, 4, u64> component_mask; | 704 | BitField<31, 4, u64> component_mask; |
| 705 | BitField<35, 1, u64> ndv_flag; | ||
| 706 | BitField<49, 1, u64> nodep_flag; | ||
| 601 | 707 | ||
| 602 | bool IsComponentEnabled(size_t component) const { | 708 | bool IsComponentEnabled(std::size_t component) const { |
| 603 | return ((1ull << component) & component_mask) != 0; | 709 | return ((1ull << component) & component_mask) != 0; |
| 604 | } | 710 | } |
| 711 | |||
| 712 | bool UsesMiscMode(TextureMiscMode mode) const { | ||
| 713 | switch (mode) { | ||
| 714 | case TextureMiscMode::NDV: | ||
| 715 | return (ndv_flag != 0); | ||
| 716 | case TextureMiscMode::NODEP: | ||
| 717 | return (nodep_flag != 0); | ||
| 718 | default: | ||
| 719 | break; | ||
| 720 | } | ||
| 721 | return false; | ||
| 722 | } | ||
| 605 | } tmml; | 723 | } tmml; |
| 606 | 724 | ||
| 607 | union { | 725 | union { |
| 608 | BitField<28, 1, u64> array; | 726 | BitField<28, 1, u64> array; |
| 609 | BitField<29, 2, TextureType> texture_type; | 727 | BitField<29, 2, TextureType> texture_type; |
| 728 | BitField<35, 1, u64> ndv_flag; | ||
| 729 | BitField<49, 1, u64> nodep_flag; | ||
| 730 | BitField<50, 1, u64> dc_flag; | ||
| 731 | BitField<54, 2, u64> info; | ||
| 610 | BitField<56, 2, u64> component; | 732 | BitField<56, 2, u64> component; |
| 733 | |||
| 734 | bool UsesMiscMode(TextureMiscMode mode) const { | ||
| 735 | switch (mode) { | ||
| 736 | case TextureMiscMode::NDV: | ||
| 737 | return ndv_flag != 0; | ||
| 738 | case TextureMiscMode::NODEP: | ||
| 739 | return nodep_flag != 0; | ||
| 740 | case TextureMiscMode::DC: | ||
| 741 | return dc_flag != 0; | ||
| 742 | case TextureMiscMode::AOFFI: | ||
| 743 | return info == 1; | ||
| 744 | case TextureMiscMode::PTP: | ||
| 745 | return info == 2; | ||
| 746 | default: | ||
| 747 | break; | ||
| 748 | } | ||
| 749 | return false; | ||
| 750 | } | ||
| 611 | } tld4; | 751 | } tld4; |
| 612 | 752 | ||
| 613 | union { | 753 | union { |
| 754 | BitField<49, 1, u64> nodep_flag; | ||
| 755 | BitField<50, 1, u64> dc_flag; | ||
| 756 | BitField<51, 1, u64> aoffi_flag; | ||
| 614 | BitField<52, 2, u64> component; | 757 | BitField<52, 2, u64> component; |
| 758 | |||
| 759 | bool UsesMiscMode(TextureMiscMode mode) const { | ||
| 760 | switch (mode) { | ||
| 761 | case TextureMiscMode::DC: | ||
| 762 | return dc_flag != 0; | ||
| 763 | case TextureMiscMode::NODEP: | ||
| 764 | return nodep_flag != 0; | ||
| 765 | case TextureMiscMode::AOFFI: | ||
| 766 | return aoffi_flag != 0; | ||
| 767 | default: | ||
| 768 | break; | ||
| 769 | } | ||
| 770 | return false; | ||
| 771 | } | ||
| 615 | } tld4s; | 772 | } tld4s; |
| 616 | 773 | ||
| 617 | union { | 774 | union { |
| 618 | BitField<0, 8, Register> gpr0; | 775 | BitField<0, 8, Register> gpr0; |
| 619 | BitField<28, 8, Register> gpr28; | 776 | BitField<28, 8, Register> gpr28; |
| 620 | BitField<49, 1, u64> nodep; | 777 | BitField<49, 1, u64> nodep_flag; |
| 621 | BitField<50, 3, u64> component_mask_selector; | 778 | BitField<50, 3, u64> component_mask_selector; |
| 622 | BitField<53, 4, u64> texture_info; | 779 | BitField<53, 4, u64> texture_info; |
| 623 | 780 | ||
| @@ -637,6 +794,37 @@ union Instruction { | |||
| 637 | UNREACHABLE(); | 794 | UNREACHABLE(); |
| 638 | } | 795 | } |
| 639 | 796 | ||
| 797 | TextureProcessMode GetTextureProcessMode() const { | ||
| 798 | switch (texture_info) { | ||
| 799 | case 0: | ||
| 800 | case 2: | ||
| 801 | case 6: | ||
| 802 | case 8: | ||
| 803 | case 9: | ||
| 804 | case 11: | ||
| 805 | return TextureProcessMode::LZ; | ||
| 806 | case 3: | ||
| 807 | case 5: | ||
| 808 | case 13: | ||
| 809 | return TextureProcessMode::LL; | ||
| 810 | default: | ||
| 811 | break; | ||
| 812 | } | ||
| 813 | return TextureProcessMode::None; | ||
| 814 | } | ||
| 815 | |||
| 816 | bool UsesMiscMode(TextureMiscMode mode) const { | ||
| 817 | switch (mode) { | ||
| 818 | case TextureMiscMode::DC: | ||
| 819 | return (texture_info >= 4 && texture_info <= 6) || texture_info == 9; | ||
| 820 | case TextureMiscMode::NODEP: | ||
| 821 | return nodep_flag != 0; | ||
| 822 | default: | ||
| 823 | break; | ||
| 824 | } | ||
| 825 | return false; | ||
| 826 | } | ||
| 827 | |||
| 640 | bool IsArrayTexture() const { | 828 | bool IsArrayTexture() const { |
| 641 | // TEXS only supports Texture2D arrays. | 829 | // TEXS only supports Texture2D arrays. |
| 642 | return texture_info >= 7 && texture_info <= 9; | 830 | return texture_info >= 7 && texture_info <= 9; |
| @@ -646,7 +834,7 @@ union Instruction { | |||
| 646 | return gpr28.Value() != Register::ZeroIndex; | 834 | return gpr28.Value() != Register::ZeroIndex; |
| 647 | } | 835 | } |
| 648 | 836 | ||
| 649 | bool IsComponentEnabled(size_t component) const { | 837 | bool IsComponentEnabled(std::size_t component) const { |
| 650 | static constexpr std::array<std::array<u32, 8>, 4> mask_lut{{ | 838 | static constexpr std::array<std::array<u32, 8>, 4> mask_lut{{ |
| 651 | {}, | 839 | {}, |
| 652 | {0x1, 0x2, 0x4, 0x8, 0x3, 0x9, 0xa, 0xc}, | 840 | {0x1, 0x2, 0x4, 0x8, 0x3, 0x9, 0xa, 0xc}, |
| @@ -654,7 +842,7 @@ union Instruction { | |||
| 654 | {0x7, 0xb, 0xd, 0xe, 0xf}, | 842 | {0x7, 0xb, 0xd, 0xe, 0xf}, |
| 655 | }}; | 843 | }}; |
| 656 | 844 | ||
| 657 | size_t index{gpr0.Value() != Register::ZeroIndex ? 1U : 0U}; | 845 | std::size_t index{gpr0.Value() != Register::ZeroIndex ? 1U : 0U}; |
| 658 | index |= gpr28.Value() != Register::ZeroIndex ? 2 : 0; | 846 | index |= gpr28.Value() != Register::ZeroIndex ? 2 : 0; |
| 659 | 847 | ||
| 660 | u32 mask = mask_lut[index][component_mask_selector]; | 848 | u32 mask = mask_lut[index][component_mask_selector]; |
| @@ -665,6 +853,7 @@ union Instruction { | |||
| 665 | } texs; | 853 | } texs; |
| 666 | 854 | ||
| 667 | union { | 855 | union { |
| 856 | BitField<49, 1, u64> nodep_flag; | ||
| 668 | BitField<53, 4, u64> texture_info; | 857 | BitField<53, 4, u64> texture_info; |
| 669 | 858 | ||
| 670 | TextureType GetTextureType() const { | 859 | TextureType GetTextureType() const { |
| @@ -685,6 +874,26 @@ union Instruction { | |||
| 685 | UNREACHABLE(); | 874 | UNREACHABLE(); |
| 686 | } | 875 | } |
| 687 | 876 | ||
| 877 | TextureProcessMode GetTextureProcessMode() const { | ||
| 878 | if (texture_info == 1 || texture_info == 5 || texture_info == 12) | ||
| 879 | return TextureProcessMode::LL; | ||
| 880 | return TextureProcessMode::LZ; | ||
| 881 | } | ||
| 882 | |||
| 883 | bool UsesMiscMode(TextureMiscMode mode) const { | ||
| 884 | switch (mode) { | ||
| 885 | case TextureMiscMode::AOFFI: | ||
| 886 | return texture_info == 12 || texture_info == 4; | ||
| 887 | case TextureMiscMode::MZ: | ||
| 888 | return texture_info == 5; | ||
| 889 | case TextureMiscMode::NODEP: | ||
| 890 | return nodep_flag != 0; | ||
| 891 | default: | ||
| 892 | break; | ||
| 893 | } | ||
| 894 | return false; | ||
| 895 | } | ||
| 896 | |||
| 688 | bool IsArrayTexture() const { | 897 | bool IsArrayTexture() const { |
| 689 | // TEXS only supports Texture2D arrays. | 898 | // TEXS only supports Texture2D arrays. |
| 690 | return texture_info == 8; | 899 | return texture_info == 8; |
| @@ -727,6 +936,7 @@ union Instruction { | |||
| 727 | BitField<36, 5, u64> index; | 936 | BitField<36, 5, u64> index; |
| 728 | } cbuf36; | 937 | } cbuf36; |
| 729 | 938 | ||
| 939 | BitField<47, 1, u64> generates_cc; | ||
| 730 | BitField<61, 1, u64> is_b_imm; | 940 | BitField<61, 1, u64> is_b_imm; |
| 731 | BitField<60, 1, u64> is_b_gpr; | 941 | BitField<60, 1, u64> is_b_gpr; |
| 732 | BitField<59, 1, u64> is_c_gpr; | 942 | BitField<59, 1, u64> is_c_gpr; |
| @@ -851,6 +1061,7 @@ public: | |||
| 851 | ISET_IMM, | 1061 | ISET_IMM, |
| 852 | PSETP, | 1062 | PSETP, |
| 853 | PSET, | 1063 | PSET, |
| 1064 | CSETP, | ||
| 854 | XMAD_IMM, | 1065 | XMAD_IMM, |
| 855 | XMAD_CR, | 1066 | XMAD_CR, |
| 856 | XMAD_RC, | 1067 | XMAD_RC, |
| @@ -939,7 +1150,7 @@ public: | |||
| 939 | private: | 1150 | private: |
| 940 | struct Detail { | 1151 | struct Detail { |
| 941 | private: | 1152 | private: |
| 942 | static constexpr size_t opcode_bitsize = 16; | 1153 | static constexpr std::size_t opcode_bitsize = 16; |
| 943 | 1154 | ||
| 944 | /** | 1155 | /** |
| 945 | * Generates the mask and the expected value after masking from a given bitstring. | 1156 | * Generates the mask and the expected value after masking from a given bitstring. |
| @@ -948,8 +1159,8 @@ private: | |||
| 948 | */ | 1159 | */ |
| 949 | static auto GetMaskAndExpect(const char* const bitstring) { | 1160 | static auto GetMaskAndExpect(const char* const bitstring) { |
| 950 | u16 mask = 0, expect = 0; | 1161 | u16 mask = 0, expect = 0; |
| 951 | for (size_t i = 0; i < opcode_bitsize; i++) { | 1162 | for (std::size_t i = 0; i < opcode_bitsize; i++) { |
| 952 | const size_t bit_position = opcode_bitsize - i - 1; | 1163 | const std::size_t bit_position = opcode_bitsize - i - 1; |
| 953 | switch (bitstring[i]) { | 1164 | switch (bitstring[i]) { |
| 954 | case '0': | 1165 | case '0': |
| 955 | mask |= 1 << bit_position; | 1166 | mask |= 1 << bit_position; |
| @@ -1087,6 +1298,7 @@ private: | |||
| 1087 | INST("0011011-0101----", Id::ISET_IMM, Type::IntegerSet, "ISET_IMM"), | 1298 | INST("0011011-0101----", Id::ISET_IMM, Type::IntegerSet, "ISET_IMM"), |
| 1088 | INST("0101000010001---", Id::PSET, Type::PredicateSetRegister, "PSET"), | 1299 | INST("0101000010001---", Id::PSET, Type::PredicateSetRegister, "PSET"), |
| 1089 | INST("0101000010010---", Id::PSETP, Type::PredicateSetPredicate, "PSETP"), | 1300 | INST("0101000010010---", Id::PSETP, Type::PredicateSetPredicate, "PSETP"), |
| 1301 | INST("010100001010----", Id::CSETP, Type::PredicateSetPredicate, "CSETP"), | ||
| 1090 | INST("0011011-00------", Id::XMAD_IMM, Type::Xmad, "XMAD_IMM"), | 1302 | INST("0011011-00------", Id::XMAD_IMM, Type::Xmad, "XMAD_IMM"), |
| 1091 | INST("0100111---------", Id::XMAD_CR, Type::Xmad, "XMAD_CR"), | 1303 | INST("0100111---------", Id::XMAD_CR, Type::Xmad, "XMAD_CR"), |
| 1092 | INST("010100010-------", Id::XMAD_RC, Type::Xmad, "XMAD_RC"), | 1304 | INST("010100010-------", Id::XMAD_RC, Type::Xmad, "XMAD_RC"), |
diff --git a/src/video_core/engines/shader_header.h b/src/video_core/engines/shader_header.h new file mode 100644 index 000000000..a885ee3cf --- /dev/null +++ b/src/video_core/engines/shader_header.h | |||
| @@ -0,0 +1,103 @@ | |||
| 1 | // Copyright 2018 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/bit_field.h" | ||
| 8 | #include "common/common_funcs.h" | ||
| 9 | #include "common/common_types.h" | ||
| 10 | |||
| 11 | namespace Tegra::Shader { | ||
| 12 | |||
| 13 | enum class OutputTopology : u32 { | ||
| 14 | PointList = 1, | ||
| 15 | LineStrip = 6, | ||
| 16 | TriangleStrip = 7, | ||
| 17 | }; | ||
| 18 | |||
| 19 | // Documentation in: | ||
| 20 | // http://download.nvidia.com/open-gpu-doc/Shader-Program-Header/1/Shader-Program-Header.html#ImapTexture | ||
| 21 | struct Header { | ||
| 22 | union { | ||
| 23 | BitField<0, 5, u32> sph_type; | ||
| 24 | BitField<5, 5, u32> version; | ||
| 25 | BitField<10, 4, u32> shader_type; | ||
| 26 | BitField<14, 1, u32> mrt_enable; | ||
| 27 | BitField<15, 1, u32> kills_pixels; | ||
| 28 | BitField<16, 1, u32> does_global_store; | ||
| 29 | BitField<17, 4, u32> sass_version; | ||
| 30 | BitField<21, 5, u32> reserved; | ||
| 31 | BitField<26, 1, u32> does_load_or_store; | ||
| 32 | BitField<27, 1, u32> does_fp64; | ||
| 33 | BitField<28, 4, u32> stream_out_mask; | ||
| 34 | } common0; | ||
| 35 | |||
| 36 | union { | ||
| 37 | BitField<0, 24, u32> shader_local_memory_low_size; | ||
| 38 | BitField<24, 8, u32> per_patch_attribute_count; | ||
| 39 | } common1; | ||
| 40 | |||
| 41 | union { | ||
| 42 | BitField<0, 24, u32> shader_local_memory_high_size; | ||
| 43 | BitField<24, 8, u32> threads_per_input_primitive; | ||
| 44 | } common2; | ||
| 45 | |||
| 46 | union { | ||
| 47 | BitField<0, 24, u32> shader_local_memory_crs_size; | ||
| 48 | BitField<24, 4, OutputTopology> output_topology; | ||
| 49 | BitField<28, 4, u32> reserved; | ||
| 50 | } common3; | ||
| 51 | |||
| 52 | union { | ||
| 53 | BitField<0, 12, u32> max_output_vertices; | ||
| 54 | BitField<12, 8, u32> store_req_start; // NOTE: not used by geometry shaders. | ||
| 55 | BitField<24, 4, u32> reserved; | ||
| 56 | BitField<12, 8, u32> store_req_end; // NOTE: not used by geometry shaders. | ||
| 57 | } common4; | ||
| 58 | |||
| 59 | union { | ||
| 60 | struct { | ||
| 61 | INSERT_PADDING_BYTES(3); // ImapSystemValuesA | ||
| 62 | INSERT_PADDING_BYTES(1); // ImapSystemValuesB | ||
| 63 | INSERT_PADDING_BYTES(16); // ImapGenericVector[32] | ||
| 64 | INSERT_PADDING_BYTES(2); // ImapColor | ||
| 65 | INSERT_PADDING_BYTES(2); // ImapSystemValuesC | ||
| 66 | INSERT_PADDING_BYTES(5); // ImapFixedFncTexture[10] | ||
| 67 | INSERT_PADDING_BYTES(1); // ImapReserved | ||
| 68 | INSERT_PADDING_BYTES(3); // OmapSystemValuesA | ||
| 69 | INSERT_PADDING_BYTES(1); // OmapSystemValuesB | ||
| 70 | INSERT_PADDING_BYTES(16); // OmapGenericVector[32] | ||
| 71 | INSERT_PADDING_BYTES(2); // OmapColor | ||
| 72 | INSERT_PADDING_BYTES(2); // OmapSystemValuesC | ||
| 73 | INSERT_PADDING_BYTES(5); // OmapFixedFncTexture[10] | ||
| 74 | INSERT_PADDING_BYTES(1); // OmapReserved | ||
| 75 | } vtg; | ||
| 76 | |||
| 77 | struct { | ||
| 78 | INSERT_PADDING_BYTES(3); // ImapSystemValuesA | ||
| 79 | INSERT_PADDING_BYTES(1); // ImapSystemValuesB | ||
| 80 | INSERT_PADDING_BYTES(32); // ImapGenericVector[32] | ||
| 81 | INSERT_PADDING_BYTES(2); // ImapColor | ||
| 82 | INSERT_PADDING_BYTES(2); // ImapSystemValuesC | ||
| 83 | INSERT_PADDING_BYTES(10); // ImapFixedFncTexture[10] | ||
| 84 | INSERT_PADDING_BYTES(2); // ImapReserved | ||
| 85 | struct { | ||
| 86 | u32 target; | ||
| 87 | union { | ||
| 88 | BitField<0, 1, u32> sample_mask; | ||
| 89 | BitField<1, 1, u32> depth; | ||
| 90 | BitField<2, 30, u32> reserved; | ||
| 91 | }; | ||
| 92 | } omap; | ||
| 93 | bool IsColorComponentOutputEnabled(u32 render_target, u32 component) const { | ||
| 94 | const u32 bit = render_target * 4 + component; | ||
| 95 | return omap.target & (1 << bit); | ||
| 96 | } | ||
| 97 | } ps; | ||
| 98 | }; | ||
| 99 | }; | ||
| 100 | |||
| 101 | static_assert(sizeof(Header) == 0x50, "Incorrect structure size"); | ||
| 102 | |||
| 103 | } // namespace Tegra::Shader | ||
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 86a809f86..baa8b63b7 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include "common/assert.h" | 5 | #include "common/assert.h" |
| 6 | #include "video_core/engines/fermi_2d.h" | 6 | #include "video_core/engines/fermi_2d.h" |
| 7 | #include "video_core/engines/kepler_memory.h" | ||
| 7 | #include "video_core/engines/maxwell_3d.h" | 8 | #include "video_core/engines/maxwell_3d.h" |
| 8 | #include "video_core/engines/maxwell_compute.h" | 9 | #include "video_core/engines/maxwell_compute.h" |
| 9 | #include "video_core/engines/maxwell_dma.h" | 10 | #include "video_core/engines/maxwell_dma.h" |
| @@ -27,6 +28,7 @@ GPU::GPU(VideoCore::RasterizerInterface& rasterizer) { | |||
| 27 | fermi_2d = std::make_unique<Engines::Fermi2D>(*memory_manager); | 28 | fermi_2d = std::make_unique<Engines::Fermi2D>(*memory_manager); |
| 28 | maxwell_compute = std::make_unique<Engines::MaxwellCompute>(); | 29 | maxwell_compute = std::make_unique<Engines::MaxwellCompute>(); |
| 29 | maxwell_dma = std::make_unique<Engines::MaxwellDMA>(*memory_manager); | 30 | maxwell_dma = std::make_unique<Engines::MaxwellDMA>(*memory_manager); |
| 31 | kepler_memory = std::make_unique<Engines::KeplerMemory>(*memory_manager); | ||
| 30 | } | 32 | } |
| 31 | 33 | ||
| 32 | GPU::~GPU() = default; | 34 | GPU::~GPU() = default; |
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index 589a59b4f..5cc1e19ca 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h | |||
| @@ -42,6 +42,7 @@ enum class RenderTargetFormat : u32 { | |||
| 42 | R32_UINT = 0xE4, | 42 | R32_UINT = 0xE4, |
| 43 | R32_FLOAT = 0xE5, | 43 | R32_FLOAT = 0xE5, |
| 44 | B5G6R5_UNORM = 0xE8, | 44 | B5G6R5_UNORM = 0xE8, |
| 45 | BGR5A1_UNORM = 0xE9, | ||
| 45 | RG8_UNORM = 0xEA, | 46 | RG8_UNORM = 0xEA, |
| 46 | RG8_SNORM = 0xEB, | 47 | RG8_SNORM = 0xEB, |
| 47 | R16_UNORM = 0xEE, | 48 | R16_UNORM = 0xEE, |
| @@ -102,6 +103,7 @@ class Fermi2D; | |||
| 102 | class Maxwell3D; | 103 | class Maxwell3D; |
| 103 | class MaxwellCompute; | 104 | class MaxwellCompute; |
| 104 | class MaxwellDMA; | 105 | class MaxwellDMA; |
| 106 | class KeplerMemory; | ||
| 105 | } // namespace Engines | 107 | } // namespace Engines |
| 106 | 108 | ||
| 107 | enum class EngineID { | 109 | enum class EngineID { |
| @@ -146,6 +148,8 @@ private: | |||
| 146 | std::unique_ptr<Engines::MaxwellCompute> maxwell_compute; | 148 | std::unique_ptr<Engines::MaxwellCompute> maxwell_compute; |
| 147 | /// DMA engine | 149 | /// DMA engine |
| 148 | std::unique_ptr<Engines::MaxwellDMA> maxwell_dma; | 150 | std::unique_ptr<Engines::MaxwellDMA> maxwell_dma; |
| 151 | /// Inline memory engine | ||
| 152 | std::unique_ptr<Engines::KeplerMemory> kepler_memory; | ||
| 149 | }; | 153 | }; |
| 150 | 154 | ||
| 151 | } // namespace Tegra | 155 | } // namespace Tegra |
diff --git a/src/video_core/macro_interpreter.h b/src/video_core/macro_interpreter.h index 7d836b816..cee0baaf3 100644 --- a/src/video_core/macro_interpreter.h +++ b/src/video_core/macro_interpreter.h | |||
| @@ -152,7 +152,7 @@ private: | |||
| 152 | boost::optional<u32> | 152 | boost::optional<u32> |
| 153 | delayed_pc; ///< Program counter to execute at after the delay slot is executed. | 153 | delayed_pc; ///< Program counter to execute at after the delay slot is executed. |
| 154 | 154 | ||
| 155 | static constexpr size_t NumMacroRegisters = 8; | 155 | static constexpr std::size_t NumMacroRegisters = 8; |
| 156 | 156 | ||
| 157 | /// General purpose macro registers. | 157 | /// General purpose macro registers. |
| 158 | std::array<u32, NumMacroRegisters> registers = {}; | 158 | std::array<u32, NumMacroRegisters> registers = {}; |
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp index 0b5d18bcb..578aca789 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp | |||
| @@ -12,10 +12,10 @@ | |||
| 12 | 12 | ||
| 13 | namespace OpenGL { | 13 | namespace OpenGL { |
| 14 | 14 | ||
| 15 | OGLBufferCache::OGLBufferCache(size_t size) : stream_buffer(GL_ARRAY_BUFFER, size) {} | 15 | OGLBufferCache::OGLBufferCache(std::size_t size) : stream_buffer(GL_ARRAY_BUFFER, size) {} |
| 16 | 16 | ||
| 17 | GLintptr OGLBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, size_t size, size_t alignment, | 17 | GLintptr OGLBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size, |
| 18 | bool cache) { | 18 | std::size_t alignment, bool cache) { |
| 19 | auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager(); | 19 | auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager(); |
| 20 | const boost::optional<VAddr> cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)}; | 20 | const boost::optional<VAddr> cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)}; |
| 21 | 21 | ||
| @@ -53,7 +53,8 @@ GLintptr OGLBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, size_t size, siz | |||
| 53 | return uploaded_offset; | 53 | return uploaded_offset; |
| 54 | } | 54 | } |
| 55 | 55 | ||
| 56 | GLintptr OGLBufferCache::UploadHostMemory(const void* raw_pointer, size_t size, size_t alignment) { | 56 | GLintptr OGLBufferCache::UploadHostMemory(const void* raw_pointer, std::size_t size, |
| 57 | std::size_t alignment) { | ||
| 57 | AlignBuffer(alignment); | 58 | AlignBuffer(alignment); |
| 58 | std::memcpy(buffer_ptr, raw_pointer, size); | 59 | std::memcpy(buffer_ptr, raw_pointer, size); |
| 59 | GLintptr uploaded_offset = buffer_offset; | 60 | GLintptr uploaded_offset = buffer_offset; |
| @@ -63,7 +64,7 @@ GLintptr OGLBufferCache::UploadHostMemory(const void* raw_pointer, size_t size, | |||
| 63 | return uploaded_offset; | 64 | return uploaded_offset; |
| 64 | } | 65 | } |
| 65 | 66 | ||
| 66 | void OGLBufferCache::Map(size_t max_size) { | 67 | void OGLBufferCache::Map(std::size_t max_size) { |
| 67 | bool invalidate; | 68 | bool invalidate; |
| 68 | std::tie(buffer_ptr, buffer_offset_base, invalidate) = | 69 | std::tie(buffer_ptr, buffer_offset_base, invalidate) = |
| 69 | stream_buffer.Map(static_cast<GLsizeiptr>(max_size), 4); | 70 | stream_buffer.Map(static_cast<GLsizeiptr>(max_size), 4); |
| @@ -81,10 +82,10 @@ GLuint OGLBufferCache::GetHandle() const { | |||
| 81 | return stream_buffer.GetHandle(); | 82 | return stream_buffer.GetHandle(); |
| 82 | } | 83 | } |
| 83 | 84 | ||
| 84 | void OGLBufferCache::AlignBuffer(size_t alignment) { | 85 | void OGLBufferCache::AlignBuffer(std::size_t alignment) { |
| 85 | // Align the offset, not the mapped pointer | 86 | // Align the offset, not the mapped pointer |
| 86 | GLintptr offset_aligned = | 87 | GLintptr offset_aligned = |
| 87 | static_cast<GLintptr>(Common::AlignUp(static_cast<size_t>(buffer_offset), alignment)); | 88 | static_cast<GLintptr>(Common::AlignUp(static_cast<std::size_t>(buffer_offset), alignment)); |
| 88 | buffer_ptr += offset_aligned - buffer_offset; | 89 | buffer_ptr += offset_aligned - buffer_offset; |
| 89 | buffer_offset = offset_aligned; | 90 | buffer_offset = offset_aligned; |
| 90 | } | 91 | } |
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h index 6da862902..6c18461f4 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.h +++ b/src/video_core/renderer_opengl/gl_buffer_cache.h | |||
| @@ -19,32 +19,32 @@ struct CachedBufferEntry final { | |||
| 19 | return addr; | 19 | return addr; |
| 20 | } | 20 | } |
| 21 | 21 | ||
| 22 | size_t GetSizeInBytes() const { | 22 | std::size_t GetSizeInBytes() const { |
| 23 | return size; | 23 | return size; |
| 24 | } | 24 | } |
| 25 | 25 | ||
| 26 | VAddr addr; | 26 | VAddr addr; |
| 27 | size_t size; | 27 | std::size_t size; |
| 28 | GLintptr offset; | 28 | GLintptr offset; |
| 29 | size_t alignment; | 29 | std::size_t alignment; |
| 30 | }; | 30 | }; |
| 31 | 31 | ||
| 32 | class OGLBufferCache final : public RasterizerCache<std::shared_ptr<CachedBufferEntry>> { | 32 | class OGLBufferCache final : public RasterizerCache<std::shared_ptr<CachedBufferEntry>> { |
| 33 | public: | 33 | public: |
| 34 | explicit OGLBufferCache(size_t size); | 34 | explicit OGLBufferCache(std::size_t size); |
| 35 | 35 | ||
| 36 | GLintptr UploadMemory(Tegra::GPUVAddr gpu_addr, size_t size, size_t alignment = 4, | 36 | GLintptr UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size, std::size_t alignment = 4, |
| 37 | bool cache = true); | 37 | bool cache = true); |
| 38 | 38 | ||
| 39 | GLintptr UploadHostMemory(const void* raw_pointer, size_t size, size_t alignment = 4); | 39 | GLintptr UploadHostMemory(const void* raw_pointer, std::size_t size, std::size_t alignment = 4); |
| 40 | 40 | ||
| 41 | void Map(size_t max_size); | 41 | void Map(std::size_t max_size); |
| 42 | void Unmap(); | 42 | void Unmap(); |
| 43 | 43 | ||
| 44 | GLuint GetHandle() const; | 44 | GLuint GetHandle() const; |
| 45 | 45 | ||
| 46 | protected: | 46 | protected: |
| 47 | void AlignBuffer(size_t alignment); | 47 | void AlignBuffer(std::size_t alignment); |
| 48 | 48 | ||
| 49 | private: | 49 | private: |
| 50 | OGLStreamBuffer stream_buffer; | 50 | OGLStreamBuffer stream_buffer; |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 7e1bba67d..1fcd13f04 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -46,7 +46,7 @@ MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, | |||
| 46 | RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo& info) | 46 | RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo& info) |
| 47 | : emu_window{window}, screen_info{info}, buffer_cache(STREAM_BUFFER_SIZE) { | 47 | : emu_window{window}, screen_info{info}, buffer_cache(STREAM_BUFFER_SIZE) { |
| 48 | // Create sampler objects | 48 | // Create sampler objects |
| 49 | for (size_t i = 0; i < texture_samplers.size(); ++i) { | 49 | for (std::size_t i = 0; i < texture_samplers.size(); ++i) { |
| 50 | texture_samplers[i].Create(); | 50 | texture_samplers[i].Create(); |
| 51 | state.texture_units[i].sampler = texture_samplers[i].sampler.handle; | 51 | state.texture_units[i].sampler = texture_samplers[i].sampler.handle; |
| 52 | } | 52 | } |
| @@ -181,7 +181,7 @@ void RasterizerOpenGL::SetupShaders() { | |||
| 181 | u32 current_constbuffer_bindpoint = Tegra::Engines::Maxwell3D::Regs::MaxShaderStage; | 181 | u32 current_constbuffer_bindpoint = Tegra::Engines::Maxwell3D::Regs::MaxShaderStage; |
| 182 | u32 current_texture_bindpoint = 0; | 182 | u32 current_texture_bindpoint = 0; |
| 183 | 183 | ||
| 184 | for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { | 184 | for (std::size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { |
| 185 | const auto& shader_config = gpu.regs.shader_config[index]; | 185 | const auto& shader_config = gpu.regs.shader_config[index]; |
| 186 | const Maxwell::ShaderProgram program{static_cast<Maxwell::ShaderProgram>(index)}; | 186 | const Maxwell::ShaderProgram program{static_cast<Maxwell::ShaderProgram>(index)}; |
| 187 | 187 | ||
| @@ -190,12 +190,12 @@ void RasterizerOpenGL::SetupShaders() { | |||
| 190 | continue; | 190 | continue; |
| 191 | } | 191 | } |
| 192 | 192 | ||
| 193 | const size_t stage{index == 0 ? 0 : index - 1}; // Stage indices are 0 - 5 | 193 | const std::size_t stage{index == 0 ? 0 : index - 1}; // Stage indices are 0 - 5 |
| 194 | 194 | ||
| 195 | GLShader::MaxwellUniformData ubo{}; | 195 | GLShader::MaxwellUniformData ubo{}; |
| 196 | ubo.SetFromRegs(gpu.state.shader_stages[stage]); | 196 | ubo.SetFromRegs(gpu.state.shader_stages[stage]); |
| 197 | const GLintptr offset = buffer_cache.UploadHostMemory( | 197 | const GLintptr offset = buffer_cache.UploadHostMemory( |
| 198 | &ubo, sizeof(ubo), static_cast<size_t>(uniform_buffer_alignment)); | 198 | &ubo, sizeof(ubo), static_cast<std::size_t>(uniform_buffer_alignment)); |
| 199 | 199 | ||
| 200 | // Bind the buffer | 200 | // Bind the buffer |
| 201 | glBindBufferRange(GL_UNIFORM_BUFFER, stage, buffer_cache.GetHandle(), offset, sizeof(ubo)); | 201 | glBindBufferRange(GL_UNIFORM_BUFFER, stage, buffer_cache.GetHandle(), offset, sizeof(ubo)); |
| @@ -238,10 +238,10 @@ void RasterizerOpenGL::SetupShaders() { | |||
| 238 | shader_program_manager->UseTrivialGeometryShader(); | 238 | shader_program_manager->UseTrivialGeometryShader(); |
| 239 | } | 239 | } |
| 240 | 240 | ||
| 241 | size_t RasterizerOpenGL::CalculateVertexArraysSize() const { | 241 | std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const { |
| 242 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; | 242 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; |
| 243 | 243 | ||
| 244 | size_t size = 0; | 244 | std::size_t size = 0; |
| 245 | for (u32 index = 0; index < Maxwell::NumVertexArrays; ++index) { | 245 | for (u32 index = 0; index < Maxwell::NumVertexArrays; ++index) { |
| 246 | if (!regs.vertex_array[index].IsEnabled()) | 246 | if (!regs.vertex_array[index].IsEnabled()) |
| 247 | continue; | 247 | continue; |
| @@ -299,7 +299,7 @@ void RasterizerOpenGL::UpdatePagesCachedCount(VAddr addr, u64 size, int delta) { | |||
| 299 | 299 | ||
| 300 | void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_depth_fb, | 300 | void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_depth_fb, |
| 301 | bool preserve_contents, | 301 | bool preserve_contents, |
| 302 | boost::optional<size_t> single_color_target) { | 302 | boost::optional<std::size_t> single_color_target) { |
| 303 | MICROPROFILE_SCOPE(OpenGL_Framebuffer); | 303 | MICROPROFILE_SCOPE(OpenGL_Framebuffer); |
| 304 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; | 304 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; |
| 305 | 305 | ||
| @@ -330,7 +330,7 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep | |||
| 330 | } else { | 330 | } else { |
| 331 | // Multiple color attachments are enabled | 331 | // Multiple color attachments are enabled |
| 332 | std::array<GLenum, Maxwell::NumRenderTargets> buffers; | 332 | std::array<GLenum, Maxwell::NumRenderTargets> buffers; |
| 333 | for (size_t index = 0; index < Maxwell::NumRenderTargets; ++index) { | 333 | for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) { |
| 334 | Surface color_surface = res_cache.GetColorBufferSurface(index, preserve_contents); | 334 | Surface color_surface = res_cache.GetColorBufferSurface(index, preserve_contents); |
| 335 | buffers[index] = GL_COLOR_ATTACHMENT0 + regs.rt_control.GetMap(index); | 335 | buffers[index] = GL_COLOR_ATTACHMENT0 + regs.rt_control.GetMap(index); |
| 336 | glFramebufferTexture2D( | 336 | glFramebufferTexture2D( |
| @@ -342,7 +342,7 @@ void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_dep | |||
| 342 | } | 342 | } |
| 343 | } else { | 343 | } else { |
| 344 | // No color attachments are enabled - zero out all of them | 344 | // No color attachments are enabled - zero out all of them |
| 345 | for (size_t index = 0; index < Maxwell::NumRenderTargets; ++index) { | 345 | for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) { |
| 346 | glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, | 346 | glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, |
| 347 | GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(index), GL_TEXTURE_2D, | 347 | GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(index), GL_TEXTURE_2D, |
| 348 | 0, 0); | 348 | 0, 0); |
| @@ -383,7 +383,7 @@ void RasterizerOpenGL::Clear() { | |||
| 383 | bool use_stencil{}; | 383 | bool use_stencil{}; |
| 384 | 384 | ||
| 385 | OpenGLState clear_state; | 385 | OpenGLState clear_state; |
| 386 | clear_state.draw.draw_framebuffer = state.draw.draw_framebuffer; | 386 | clear_state.draw.draw_framebuffer = framebuffer.handle; |
| 387 | clear_state.color_mask.red_enabled = regs.clear_buffers.R ? GL_TRUE : GL_FALSE; | 387 | clear_state.color_mask.red_enabled = regs.clear_buffers.R ? GL_TRUE : GL_FALSE; |
| 388 | clear_state.color_mask.green_enabled = regs.clear_buffers.G ? GL_TRUE : GL_FALSE; | 388 | clear_state.color_mask.green_enabled = regs.clear_buffers.G ? GL_TRUE : GL_FALSE; |
| 389 | clear_state.color_mask.blue_enabled = regs.clear_buffers.B ? GL_TRUE : GL_FALSE; | 389 | clear_state.color_mask.blue_enabled = regs.clear_buffers.B ? GL_TRUE : GL_FALSE; |
| @@ -450,6 +450,9 @@ void RasterizerOpenGL::DrawArrays() { | |||
| 450 | SyncBlendState(); | 450 | SyncBlendState(); |
| 451 | SyncLogicOpState(); | 451 | SyncLogicOpState(); |
| 452 | SyncCullMode(); | 452 | SyncCullMode(); |
| 453 | SyncAlphaTest(); | ||
| 454 | SyncTransformFeedback(); | ||
| 455 | SyncPointState(); | ||
| 453 | 456 | ||
| 454 | // TODO(bunnei): Sync framebuffer_scale uniform here | 457 | // TODO(bunnei): Sync framebuffer_scale uniform here |
| 455 | // TODO(bunnei): Sync scissorbox uniform(s) here | 458 | // TODO(bunnei): Sync scissorbox uniform(s) here |
| @@ -462,15 +465,15 @@ void RasterizerOpenGL::DrawArrays() { | |||
| 462 | state.draw.vertex_buffer = buffer_cache.GetHandle(); | 465 | state.draw.vertex_buffer = buffer_cache.GetHandle(); |
| 463 | state.Apply(); | 466 | state.Apply(); |
| 464 | 467 | ||
| 465 | size_t buffer_size = CalculateVertexArraysSize(); | 468 | std::size_t buffer_size = CalculateVertexArraysSize(); |
| 466 | 469 | ||
| 467 | if (is_indexed) { | 470 | if (is_indexed) { |
| 468 | buffer_size = Common::AlignUp<size_t>(buffer_size, 4) + index_buffer_size; | 471 | buffer_size = Common::AlignUp<std::size_t>(buffer_size, 4) + index_buffer_size; |
| 469 | } | 472 | } |
| 470 | 473 | ||
| 471 | // Uniform space for the 5 shader stages | 474 | // Uniform space for the 5 shader stages |
| 472 | buffer_size = | 475 | buffer_size = |
| 473 | Common::AlignUp<size_t>(buffer_size, 4) + | 476 | Common::AlignUp<std::size_t>(buffer_size, 4) + |
| 474 | (sizeof(GLShader::MaxwellUniformData) + uniform_buffer_alignment) * Maxwell::MaxShaderStage; | 477 | (sizeof(GLShader::MaxwellUniformData) + uniform_buffer_alignment) * Maxwell::MaxShaderStage; |
| 475 | 478 | ||
| 476 | // Add space for at least 18 constant buffers | 479 | // Add space for at least 18 constant buffers |
| @@ -484,8 +487,13 @@ void RasterizerOpenGL::DrawArrays() { | |||
| 484 | GLintptr index_buffer_offset = 0; | 487 | GLintptr index_buffer_offset = 0; |
| 485 | if (is_indexed) { | 488 | if (is_indexed) { |
| 486 | MICROPROFILE_SCOPE(OpenGL_Index); | 489 | MICROPROFILE_SCOPE(OpenGL_Index); |
| 487 | index_buffer_offset = | 490 | |
| 488 | buffer_cache.UploadMemory(regs.index_array.StartAddress(), index_buffer_size); | 491 | // Adjust the index buffer offset so it points to the first desired index. |
| 492 | auto index_start = regs.index_array.StartAddress(); | ||
| 493 | index_start += static_cast<size_t>(regs.index_array.first) * | ||
| 494 | static_cast<size_t>(regs.index_array.FormatSizeInBytes()); | ||
| 495 | |||
| 496 | index_buffer_offset = buffer_cache.UploadMemory(index_start, index_buffer_size); | ||
| 489 | } | 497 | } |
| 490 | 498 | ||
| 491 | SetupShaders(); | 499 | SetupShaders(); |
| @@ -499,10 +507,6 @@ void RasterizerOpenGL::DrawArrays() { | |||
| 499 | if (is_indexed) { | 507 | if (is_indexed) { |
| 500 | const GLint base_vertex{static_cast<GLint>(regs.vb_element_base)}; | 508 | const GLint base_vertex{static_cast<GLint>(regs.vb_element_base)}; |
| 501 | 509 | ||
| 502 | // Adjust the index buffer offset so it points to the first desired index. | ||
| 503 | index_buffer_offset += static_cast<GLintptr>(regs.index_array.first) * | ||
| 504 | static_cast<GLintptr>(regs.index_array.FormatSizeInBytes()); | ||
| 505 | |||
| 506 | if (gpu.state.current_instance > 0) { | 510 | if (gpu.state.current_instance > 0) { |
| 507 | glDrawElementsInstancedBaseVertexBaseInstance( | 511 | glDrawElementsInstancedBaseVertexBaseInstance( |
| 508 | primitive_mode, regs.index_array.count, | 512 | primitive_mode, regs.index_array.count, |
| @@ -644,7 +648,7 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shad | |||
| 644 | MICROPROFILE_SCOPE(OpenGL_UBO); | 648 | MICROPROFILE_SCOPE(OpenGL_UBO); |
| 645 | const auto& gpu = Core::System::GetInstance().GPU(); | 649 | const auto& gpu = Core::System::GetInstance().GPU(); |
| 646 | const auto& maxwell3d = gpu.Maxwell3D(); | 650 | const auto& maxwell3d = gpu.Maxwell3D(); |
| 647 | const auto& shader_stage = maxwell3d.state.shader_stages[static_cast<size_t>(stage)]; | 651 | const auto& shader_stage = maxwell3d.state.shader_stages[static_cast<std::size_t>(stage)]; |
| 648 | const auto& entries = shader->GetShaderEntries().const_buffer_entries; | 652 | const auto& entries = shader->GetShaderEntries().const_buffer_entries; |
| 649 | 653 | ||
| 650 | constexpr u64 max_binds = Tegra::Engines::Maxwell3D::Regs::MaxConstBuffers; | 654 | constexpr u64 max_binds = Tegra::Engines::Maxwell3D::Regs::MaxConstBuffers; |
| @@ -667,7 +671,7 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shad | |||
| 667 | continue; | 671 | continue; |
| 668 | } | 672 | } |
| 669 | 673 | ||
| 670 | size_t size = 0; | 674 | std::size_t size = 0; |
| 671 | 675 | ||
| 672 | if (used_buffer.IsIndirect()) { | 676 | if (used_buffer.IsIndirect()) { |
| 673 | // Buffer is accessed indirectly, so upload the entire thing | 677 | // Buffer is accessed indirectly, so upload the entire thing |
| @@ -689,7 +693,7 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shad | |||
| 689 | ASSERT_MSG(size <= MaxConstbufferSize, "Constbuffer too big"); | 693 | ASSERT_MSG(size <= MaxConstbufferSize, "Constbuffer too big"); |
| 690 | 694 | ||
| 691 | GLintptr const_buffer_offset = buffer_cache.UploadMemory( | 695 | GLintptr const_buffer_offset = buffer_cache.UploadMemory( |
| 692 | buffer.address, size, static_cast<size_t>(uniform_buffer_alignment)); | 696 | buffer.address, size, static_cast<std::size_t>(uniform_buffer_alignment)); |
| 693 | 697 | ||
| 694 | // Now configure the bindpoint of the buffer inside the shader | 698 | // Now configure the bindpoint of the buffer inside the shader |
| 695 | glUniformBlockBinding(shader->GetProgramHandle(), | 699 | glUniformBlockBinding(shader->GetProgramHandle(), |
| @@ -882,4 +886,30 @@ void RasterizerOpenGL::SyncLogicOpState() { | |||
| 882 | state.logic_op.operation = MaxwellToGL::LogicOp(regs.logic_op.operation); | 886 | state.logic_op.operation = MaxwellToGL::LogicOp(regs.logic_op.operation); |
| 883 | } | 887 | } |
| 884 | 888 | ||
| 889 | void RasterizerOpenGL::SyncAlphaTest() { | ||
| 890 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; | ||
| 891 | |||
| 892 | // TODO(Rodrigo): Alpha testing is a legacy OpenGL feature, but it can be | ||
| 893 | // implemented with a test+discard in fragment shaders. | ||
| 894 | if (regs.alpha_test_enabled != 0) { | ||
| 895 | LOG_CRITICAL(Render_OpenGL, "Alpha testing is not implemented"); | ||
| 896 | UNREACHABLE(); | ||
| 897 | } | ||
| 898 | } | ||
| 899 | |||
| 900 | void RasterizerOpenGL::SyncTransformFeedback() { | ||
| 901 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; | ||
| 902 | |||
| 903 | if (regs.tfb_enabled != 0) { | ||
| 904 | LOG_CRITICAL(Render_OpenGL, "Transform feedbacks are not implemented"); | ||
| 905 | UNREACHABLE(); | ||
| 906 | } | ||
| 907 | } | ||
| 908 | |||
| 909 | void RasterizerOpenGL::SyncPointState() { | ||
| 910 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; | ||
| 911 | |||
| 912 | state.point.size = regs.point_size; | ||
| 913 | } | ||
| 914 | |||
| 885 | } // namespace OpenGL | 915 | } // namespace OpenGL |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 163412882..4c8ecbd1c 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h | |||
| @@ -73,7 +73,7 @@ public: | |||
| 73 | }; | 73 | }; |
| 74 | 74 | ||
| 75 | /// Maximum supported size that a constbuffer can have in bytes. | 75 | /// Maximum supported size that a constbuffer can have in bytes. |
| 76 | static constexpr size_t MaxConstbufferSize = 0x10000; | 76 | static constexpr std::size_t MaxConstbufferSize = 0x10000; |
| 77 | static_assert(MaxConstbufferSize % sizeof(GLvec4) == 0, | 77 | static_assert(MaxConstbufferSize % sizeof(GLvec4) == 0, |
| 78 | "The maximum size of a constbuffer must be a multiple of the size of GLvec4"); | 78 | "The maximum size of a constbuffer must be a multiple of the size of GLvec4"); |
| 79 | 79 | ||
| @@ -106,7 +106,7 @@ private: | |||
| 106 | */ | 106 | */ |
| 107 | void ConfigureFramebuffers(bool use_color_fb = true, bool using_depth_fb = true, | 107 | void ConfigureFramebuffers(bool use_color_fb = true, bool using_depth_fb = true, |
| 108 | bool preserve_contents = true, | 108 | bool preserve_contents = true, |
| 109 | boost::optional<size_t> single_color_target = {}); | 109 | boost::optional<std::size_t> single_color_target = {}); |
| 110 | 110 | ||
| 111 | /* | 111 | /* |
| 112 | * Configures the current constbuffers to use for the draw command. | 112 | * Configures the current constbuffers to use for the draw command. |
| @@ -158,6 +158,15 @@ private: | |||
| 158 | /// Syncs the LogicOp state to match the guest state | 158 | /// Syncs the LogicOp state to match the guest state |
| 159 | void SyncLogicOpState(); | 159 | void SyncLogicOpState(); |
| 160 | 160 | ||
| 161 | /// Syncs the alpha test state to match the guest state | ||
| 162 | void SyncAlphaTest(); | ||
| 163 | |||
| 164 | /// Syncs the transform feedback state to match the guest state | ||
| 165 | void SyncTransformFeedback(); | ||
| 166 | |||
| 167 | /// Syncs the point state to match the guest state | ||
| 168 | void SyncPointState(); | ||
| 169 | |||
| 161 | bool has_ARB_direct_state_access = false; | 170 | bool has_ARB_direct_state_access = false; |
| 162 | bool has_ARB_multi_bind = false; | 171 | bool has_ARB_multi_bind = false; |
| 163 | bool has_ARB_separate_shader_objects = false; | 172 | bool has_ARB_separate_shader_objects = false; |
| @@ -178,14 +187,14 @@ private: | |||
| 178 | OGLVertexArray> | 187 | OGLVertexArray> |
| 179 | vertex_array_cache; | 188 | vertex_array_cache; |
| 180 | 189 | ||
| 181 | std::array<SamplerInfo, GLShader::NumTextureSamplers> texture_samplers; | 190 | std::array<SamplerInfo, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> texture_samplers; |
| 182 | 191 | ||
| 183 | static constexpr size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024; | 192 | static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024; |
| 184 | OGLBufferCache buffer_cache; | 193 | OGLBufferCache buffer_cache; |
| 185 | OGLFramebuffer framebuffer; | 194 | OGLFramebuffer framebuffer; |
| 186 | GLint uniform_buffer_alignment; | 195 | GLint uniform_buffer_alignment; |
| 187 | 196 | ||
| 188 | size_t CalculateVertexArraysSize() const; | 197 | std::size_t CalculateVertexArraysSize() const; |
| 189 | 198 | ||
| 190 | void SetupVertexArrays(); | 199 | void SetupVertexArrays(); |
| 191 | 200 | ||
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 32001e44b..24a540258 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp | |||
| @@ -75,7 +75,7 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) { | |||
| 75 | return params; | 75 | return params; |
| 76 | } | 76 | } |
| 77 | 77 | ||
| 78 | /*static*/ SurfaceParams SurfaceParams::CreateForFramebuffer(size_t index) { | 78 | /*static*/ SurfaceParams SurfaceParams::CreateForFramebuffer(std::size_t index) { |
| 79 | const auto& config{Core::System::GetInstance().GPU().Maxwell3D().regs.rt[index]}; | 79 | const auto& config{Core::System::GetInstance().GPU().Maxwell3D().regs.rt[index]}; |
| 80 | SurfaceParams params{}; | 80 | SurfaceParams params{}; |
| 81 | params.addr = TryGetCpuAddr(config.Address()); | 81 | params.addr = TryGetCpuAddr(config.Address()); |
| @@ -141,8 +141,8 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form | |||
| 141 | {GL_COMPRESSED_RGBA_BPTC_UNORM_ARB, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, | 141 | {GL_COMPRESSED_RGBA_BPTC_UNORM_ARB, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, |
| 142 | true}, // BC7U | 142 | true}, // BC7U |
| 143 | {GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, | 143 | {GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, |
| 144 | ComponentType::UNorm, true}, // BC6H_UF16 | 144 | ComponentType::Float, true}, // BC6H_UF16 |
| 145 | {GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, | 145 | {GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::Float, |
| 146 | true}, // BC6H_SF16 | 146 | true}, // BC6H_SF16 |
| 147 | {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4 | 147 | {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4 |
| 148 | {GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // G8R8U | 148 | {GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // G8R8U |
| @@ -167,6 +167,7 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form | |||
| 167 | {GL_RG8, GL_RG, GL_BYTE, ComponentType::SNorm, false}, // RG8S | 167 | {GL_RG8, GL_RG, GL_BYTE, ComponentType::SNorm, false}, // RG8S |
| 168 | {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RG32UI | 168 | {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RG32UI |
| 169 | {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // R32UI | 169 | {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // R32UI |
| 170 | {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8 | ||
| 170 | 171 | ||
| 171 | // Depth formats | 172 | // Depth formats |
| 172 | {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F | 173 | {GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, ComponentType::Float, false}, // Z32F |
| @@ -203,7 +204,7 @@ static GLenum SurfaceTargetToGL(SurfaceParams::SurfaceTarget target) { | |||
| 203 | } | 204 | } |
| 204 | 205 | ||
| 205 | static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType component_type) { | 206 | static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType component_type) { |
| 206 | ASSERT(static_cast<size_t>(pixel_format) < tex_format_tuples.size()); | 207 | ASSERT(static_cast<std::size_t>(pixel_format) < tex_format_tuples.size()); |
| 207 | auto& format = tex_format_tuples[static_cast<unsigned int>(pixel_format)]; | 208 | auto& format = tex_format_tuples[static_cast<unsigned int>(pixel_format)]; |
| 208 | ASSERT(component_type == format.component_type); | 209 | ASSERT(component_type == format.component_type); |
| 209 | 210 | ||
| @@ -213,6 +214,7 @@ static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType | |||
| 213 | static bool IsPixelFormatASTC(PixelFormat format) { | 214 | static bool IsPixelFormatASTC(PixelFormat format) { |
| 214 | switch (format) { | 215 | switch (format) { |
| 215 | case PixelFormat::ASTC_2D_4X4: | 216 | case PixelFormat::ASTC_2D_4X4: |
| 217 | case PixelFormat::ASTC_2D_8X8: | ||
| 216 | return true; | 218 | return true; |
| 217 | default: | 219 | default: |
| 218 | return false; | 220 | return false; |
| @@ -223,6 +225,8 @@ static std::pair<u32, u32> GetASTCBlockSize(PixelFormat format) { | |||
| 223 | switch (format) { | 225 | switch (format) { |
| 224 | case PixelFormat::ASTC_2D_4X4: | 226 | case PixelFormat::ASTC_2D_4X4: |
| 225 | return {4, 4}; | 227 | return {4, 4}; |
| 228 | case PixelFormat::ASTC_2D_8X8: | ||
| 229 | return {8, 8}; | ||
| 226 | default: | 230 | default: |
| 227 | LOG_CRITICAL(HW_GPU, "Unhandled format: {}", static_cast<u32>(format)); | 231 | LOG_CRITICAL(HW_GPU, "Unhandled format: {}", static_cast<u32>(format)); |
| 228 | UNREACHABLE(); | 232 | UNREACHABLE(); |
| @@ -256,7 +260,7 @@ static bool IsFormatBCn(PixelFormat format) { | |||
| 256 | } | 260 | } |
| 257 | 261 | ||
| 258 | template <bool morton_to_gl, PixelFormat format> | 262 | template <bool morton_to_gl, PixelFormat format> |
| 259 | void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, size_t gl_buffer_size, | 263 | void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, std::size_t gl_buffer_size, |
| 260 | VAddr addr) { | 264 | VAddr addr) { |
| 261 | constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / CHAR_BIT; | 265 | constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / CHAR_BIT; |
| 262 | constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format); | 266 | constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format); |
| @@ -267,7 +271,7 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, size_t | |||
| 267 | const u32 tile_size{IsFormatBCn(format) ? 4U : 1U}; | 271 | const u32 tile_size{IsFormatBCn(format) ? 4U : 1U}; |
| 268 | const std::vector<u8> data = Tegra::Texture::UnswizzleTexture( | 272 | const std::vector<u8> data = Tegra::Texture::UnswizzleTexture( |
| 269 | addr, tile_size, bytes_per_pixel, stride, height, block_height); | 273 | addr, tile_size, bytes_per_pixel, stride, height, block_height); |
| 270 | const size_t size_to_copy{std::min(gl_buffer_size, data.size())}; | 274 | const std::size_t size_to_copy{std::min(gl_buffer_size, data.size())}; |
| 271 | memcpy(gl_buffer, data.data(), size_to_copy); | 275 | memcpy(gl_buffer, data.data(), size_to_copy); |
| 272 | } else { | 276 | } else { |
| 273 | // TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should | 277 | // TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should |
| @@ -278,7 +282,7 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, size_t | |||
| 278 | } | 282 | } |
| 279 | } | 283 | } |
| 280 | 284 | ||
| 281 | static constexpr std::array<void (*)(u32, u32, u32, u8*, size_t, VAddr), | 285 | static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr), |
| 282 | SurfaceParams::MaxPixelFormat> | 286 | SurfaceParams::MaxPixelFormat> |
| 283 | morton_to_gl_fns = { | 287 | morton_to_gl_fns = { |
| 284 | // clang-format off | 288 | // clang-format off |
| @@ -327,6 +331,7 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, size_t, VAddr), | |||
| 327 | MortonCopy<true, PixelFormat::RG8S>, | 331 | MortonCopy<true, PixelFormat::RG8S>, |
| 328 | MortonCopy<true, PixelFormat::RG32UI>, | 332 | MortonCopy<true, PixelFormat::RG32UI>, |
| 329 | MortonCopy<true, PixelFormat::R32UI>, | 333 | MortonCopy<true, PixelFormat::R32UI>, |
| 334 | MortonCopy<true, PixelFormat::ASTC_2D_8X8>, | ||
| 330 | MortonCopy<true, PixelFormat::Z32F>, | 335 | MortonCopy<true, PixelFormat::Z32F>, |
| 331 | MortonCopy<true, PixelFormat::Z16>, | 336 | MortonCopy<true, PixelFormat::Z16>, |
| 332 | MortonCopy<true, PixelFormat::Z24S8>, | 337 | MortonCopy<true, PixelFormat::Z24S8>, |
| @@ -335,7 +340,7 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, size_t, VAddr), | |||
| 335 | // clang-format on | 340 | // clang-format on |
| 336 | }; | 341 | }; |
| 337 | 342 | ||
| 338 | static constexpr std::array<void (*)(u32, u32, u32, u8*, size_t, VAddr), | 343 | static constexpr std::array<void (*)(u32, u32, u32, u8*, std::size_t, VAddr), |
| 339 | SurfaceParams::MaxPixelFormat> | 344 | SurfaceParams::MaxPixelFormat> |
| 340 | gl_to_morton_fns = { | 345 | gl_to_morton_fns = { |
| 341 | // clang-format off | 346 | // clang-format off |
| @@ -386,6 +391,7 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, size_t, VAddr), | |||
| 386 | MortonCopy<false, PixelFormat::RG8S>, | 391 | MortonCopy<false, PixelFormat::RG8S>, |
| 387 | MortonCopy<false, PixelFormat::RG32UI>, | 392 | MortonCopy<false, PixelFormat::RG32UI>, |
| 388 | MortonCopy<false, PixelFormat::R32UI>, | 393 | MortonCopy<false, PixelFormat::R32UI>, |
| 394 | nullptr, | ||
| 389 | MortonCopy<false, PixelFormat::Z32F>, | 395 | MortonCopy<false, PixelFormat::Z32F>, |
| 390 | MortonCopy<false, PixelFormat::Z16>, | 396 | MortonCopy<false, PixelFormat::Z16>, |
| 391 | MortonCopy<false, PixelFormat::Z24S8>, | 397 | MortonCopy<false, PixelFormat::Z24S8>, |
| @@ -495,6 +501,9 @@ CachedSurface::CachedSurface(const SurfaceParams& params) | |||
| 495 | glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_MIN_FILTER, GL_LINEAR); | 501 | glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
| 496 | glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | 502 | glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| 497 | glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | 503 | glTexParameteri(SurfaceTargetToGL(params.target), GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| 504 | |||
| 505 | VideoCore::LabelGLObject(GL_TEXTURE, texture.handle, params.addr, | ||
| 506 | SurfaceParams::SurfaceTargetName(params.target)); | ||
| 498 | } | 507 | } |
| 499 | 508 | ||
| 500 | static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) { | 509 | static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) { |
| @@ -513,9 +522,9 @@ static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) { | |||
| 513 | S8Z24 input_pixel{}; | 522 | S8Z24 input_pixel{}; |
| 514 | Z24S8 output_pixel{}; | 523 | Z24S8 output_pixel{}; |
| 515 | constexpr auto bpp{CachedSurface::GetGLBytesPerPixel(PixelFormat::S8Z24)}; | 524 | constexpr auto bpp{CachedSurface::GetGLBytesPerPixel(PixelFormat::S8Z24)}; |
| 516 | for (size_t y = 0; y < height; ++y) { | 525 | for (std::size_t y = 0; y < height; ++y) { |
| 517 | for (size_t x = 0; x < width; ++x) { | 526 | for (std::size_t x = 0; x < width; ++x) { |
| 518 | const size_t offset{bpp * (y * width + x)}; | 527 | const std::size_t offset{bpp * (y * width + x)}; |
| 519 | std::memcpy(&input_pixel, &data[offset], sizeof(S8Z24)); | 528 | std::memcpy(&input_pixel, &data[offset], sizeof(S8Z24)); |
| 520 | output_pixel.s8.Assign(input_pixel.s8); | 529 | output_pixel.s8.Assign(input_pixel.s8); |
| 521 | output_pixel.z24.Assign(input_pixel.z24); | 530 | output_pixel.z24.Assign(input_pixel.z24); |
| @@ -526,9 +535,9 @@ static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) { | |||
| 526 | 535 | ||
| 527 | static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) { | 536 | static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) { |
| 528 | constexpr auto bpp{CachedSurface::GetGLBytesPerPixel(PixelFormat::G8R8U)}; | 537 | constexpr auto bpp{CachedSurface::GetGLBytesPerPixel(PixelFormat::G8R8U)}; |
| 529 | for (size_t y = 0; y < height; ++y) { | 538 | for (std::size_t y = 0; y < height; ++y) { |
| 530 | for (size_t x = 0; x < width; ++x) { | 539 | for (std::size_t x = 0; x < width; ++x) { |
| 531 | const size_t offset{bpp * (y * width + x)}; | 540 | const std::size_t offset{bpp * (y * width + x)}; |
| 532 | const u8 temp{data[offset]}; | 541 | const u8 temp{data[offset]}; |
| 533 | data[offset] = data[offset + 1]; | 542 | data[offset] = data[offset + 1]; |
| 534 | data[offset + 1] = temp; | 543 | data[offset + 1] = temp; |
| @@ -544,7 +553,8 @@ static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) { | |||
| 544 | static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelFormat pixel_format, | 553 | static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelFormat pixel_format, |
| 545 | u32 width, u32 height) { | 554 | u32 width, u32 height) { |
| 546 | switch (pixel_format) { | 555 | switch (pixel_format) { |
| 547 | case PixelFormat::ASTC_2D_4X4: { | 556 | case PixelFormat::ASTC_2D_4X4: |
| 557 | case PixelFormat::ASTC_2D_8X8: { | ||
| 548 | // Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC. | 558 | // Convert ASTC pixel formats to RGBA8, as most desktop GPUs do not support ASTC. |
| 549 | u32 block_width{}; | 559 | u32 block_width{}; |
| 550 | u32 block_height{}; | 560 | u32 block_height{}; |
| @@ -591,13 +601,13 @@ void CachedSurface::LoadGLBuffer() { | |||
| 591 | UNREACHABLE(); | 601 | UNREACHABLE(); |
| 592 | } | 602 | } |
| 593 | 603 | ||
| 594 | gl_buffer.resize(static_cast<size_t>(params.depth) * copy_size); | 604 | gl_buffer.resize(static_cast<std::size_t>(params.depth) * copy_size); |
| 595 | morton_to_gl_fns[static_cast<size_t>(params.pixel_format)]( | 605 | morton_to_gl_fns[static_cast<std::size_t>(params.pixel_format)]( |
| 596 | params.width, params.block_height, params.height, gl_buffer.data(), copy_size, | 606 | params.width, params.block_height, params.height, gl_buffer.data(), copy_size, |
| 597 | params.addr); | 607 | params.addr); |
| 598 | } else { | 608 | } else { |
| 599 | const u8* const texture_src_data_end{texture_src_data + | 609 | const u8* const texture_src_data_end{texture_src_data + |
| 600 | (static_cast<size_t>(params.depth) * copy_size)}; | 610 | (static_cast<std::size_t>(params.depth) * copy_size)}; |
| 601 | gl_buffer.assign(texture_src_data, texture_src_data_end); | 611 | gl_buffer.assign(texture_src_data, texture_src_data_end); |
| 602 | } | 612 | } |
| 603 | 613 | ||
| @@ -616,7 +626,7 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle | |||
| 616 | 626 | ||
| 617 | MICROPROFILE_SCOPE(OpenGL_TextureUL); | 627 | MICROPROFILE_SCOPE(OpenGL_TextureUL); |
| 618 | 628 | ||
| 619 | ASSERT(gl_buffer.size() == static_cast<size_t>(params.width) * params.height * | 629 | ASSERT(gl_buffer.size() == static_cast<std::size_t>(params.width) * params.height * |
| 620 | GetGLBytesPerPixel(params.pixel_format) * params.depth); | 630 | GetGLBytesPerPixel(params.pixel_format) * params.depth); |
| 621 | 631 | ||
| 622 | const auto& rect{params.GetRect()}; | 632 | const auto& rect{params.GetRect()}; |
| @@ -624,8 +634,9 @@ void CachedSurface::UploadGLTexture(GLuint read_fb_handle, GLuint draw_fb_handle | |||
| 624 | // Load data from memory to the surface | 634 | // Load data from memory to the surface |
| 625 | const GLint x0 = static_cast<GLint>(rect.left); | 635 | const GLint x0 = static_cast<GLint>(rect.left); |
| 626 | const GLint y0 = static_cast<GLint>(rect.bottom); | 636 | const GLint y0 = static_cast<GLint>(rect.bottom); |
| 627 | const size_t buffer_offset = | 637 | const std::size_t buffer_offset = |
| 628 | static_cast<size_t>(static_cast<size_t>(y0) * params.width + static_cast<size_t>(x0)) * | 638 | static_cast<std::size_t>(static_cast<std::size_t>(y0) * params.width + |
| 639 | static_cast<std::size_t>(x0)) * | ||
| 629 | GetGLBytesPerPixel(params.pixel_format); | 640 | GetGLBytesPerPixel(params.pixel_format); |
| 630 | 641 | ||
| 631 | const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type); | 642 | const FormatTuple& tuple = GetFormatTuple(params.pixel_format, params.component_type); |
| @@ -727,7 +738,7 @@ Surface RasterizerCacheOpenGL::GetDepthBufferSurface(bool preserve_contents) { | |||
| 727 | return GetSurface(depth_params, preserve_contents); | 738 | return GetSurface(depth_params, preserve_contents); |
| 728 | } | 739 | } |
| 729 | 740 | ||
| 730 | Surface RasterizerCacheOpenGL::GetColorBufferSurface(size_t index, bool preserve_contents) { | 741 | Surface RasterizerCacheOpenGL::GetColorBufferSurface(std::size_t index, bool preserve_contents) { |
| 731 | const auto& regs{Core::System::GetInstance().GPU().Maxwell3D().regs}; | 742 | const auto& regs{Core::System::GetInstance().GPU().Maxwell3D().regs}; |
| 732 | 743 | ||
| 733 | ASSERT(index < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets); | 744 | ASSERT(index < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets); |
| @@ -825,7 +836,7 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& surface, | |||
| 825 | auto source_format = GetFormatTuple(params.pixel_format, params.component_type); | 836 | auto source_format = GetFormatTuple(params.pixel_format, params.component_type); |
| 826 | auto dest_format = GetFormatTuple(new_params.pixel_format, new_params.component_type); | 837 | auto dest_format = GetFormatTuple(new_params.pixel_format, new_params.component_type); |
| 827 | 838 | ||
| 828 | size_t buffer_size = std::max(params.SizeInBytes(), new_params.SizeInBytes()); | 839 | std::size_t buffer_size = std::max(params.SizeInBytes(), new_params.SizeInBytes()); |
| 829 | 840 | ||
| 830 | glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo.handle); | 841 | glBindBuffer(GL_PIXEL_PACK_BUFFER, copy_pbo.handle); |
| 831 | glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB); | 842 | glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB); |
| @@ -849,7 +860,7 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& surface, | |||
| 849 | LOG_DEBUG(HW_GPU, "Trying to upload extra texture data from the CPU during " | 860 | LOG_DEBUG(HW_GPU, "Trying to upload extra texture data from the CPU during " |
| 850 | "reinterpretation but the texture is tiled."); | 861 | "reinterpretation but the texture is tiled."); |
| 851 | } | 862 | } |
| 852 | size_t remaining_size = new_params.SizeInBytes() - params.SizeInBytes(); | 863 | std::size_t remaining_size = new_params.SizeInBytes() - params.SizeInBytes(); |
| 853 | std::vector<u8> data(remaining_size); | 864 | std::vector<u8> data(remaining_size); |
| 854 | Memory::ReadBlock(new_params.addr + params.SizeInBytes(), data.data(), data.size()); | 865 | Memory::ReadBlock(new_params.addr + params.SizeInBytes(), data.data(), data.size()); |
| 855 | glBufferSubData(GL_PIXEL_PACK_BUFFER, params.SizeInBytes(), remaining_size, | 866 | glBufferSubData(GL_PIXEL_PACK_BUFFER, params.SizeInBytes(), remaining_size, |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index 57ea8593b..80c5f324b 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h | |||
| @@ -70,19 +70,20 @@ struct SurfaceParams { | |||
| 70 | RG8S = 42, | 70 | RG8S = 42, |
| 71 | RG32UI = 43, | 71 | RG32UI = 43, |
| 72 | R32UI = 44, | 72 | R32UI = 44, |
| 73 | ASTC_2D_8X8 = 45, | ||
| 73 | 74 | ||
| 74 | MaxColorFormat, | 75 | MaxColorFormat, |
| 75 | 76 | ||
| 76 | // Depth formats | 77 | // Depth formats |
| 77 | Z32F = 45, | 78 | Z32F = 46, |
| 78 | Z16 = 46, | 79 | Z16 = 47, |
| 79 | 80 | ||
| 80 | MaxDepthFormat, | 81 | MaxDepthFormat, |
| 81 | 82 | ||
| 82 | // DepthStencil formats | 83 | // DepthStencil formats |
| 83 | Z24S8 = 47, | 84 | Z24S8 = 48, |
| 84 | S8Z24 = 48, | 85 | S8Z24 = 49, |
| 85 | Z32FS8 = 49, | 86 | Z32FS8 = 50, |
| 86 | 87 | ||
| 87 | MaxDepthStencilFormat, | 88 | MaxDepthStencilFormat, |
| 88 | 89 | ||
| @@ -90,7 +91,7 @@ struct SurfaceParams { | |||
| 90 | Invalid = 255, | 91 | Invalid = 255, |
| 91 | }; | 92 | }; |
| 92 | 93 | ||
| 93 | static constexpr size_t MaxPixelFormat = static_cast<size_t>(PixelFormat::Max); | 94 | static constexpr std::size_t MaxPixelFormat = static_cast<std::size_t>(PixelFormat::Max); |
| 94 | 95 | ||
| 95 | enum class ComponentType { | 96 | enum class ComponentType { |
| 96 | Invalid = 0, | 97 | Invalid = 0, |
| @@ -136,6 +137,27 @@ struct SurfaceParams { | |||
| 136 | } | 137 | } |
| 137 | } | 138 | } |
| 138 | 139 | ||
| 140 | static std::string SurfaceTargetName(SurfaceTarget target) { | ||
| 141 | switch (target) { | ||
| 142 | case SurfaceTarget::Texture1D: | ||
| 143 | return "Texture1D"; | ||
| 144 | case SurfaceTarget::Texture2D: | ||
| 145 | return "Texture2D"; | ||
| 146 | case SurfaceTarget::Texture3D: | ||
| 147 | return "Texture3D"; | ||
| 148 | case SurfaceTarget::Texture1DArray: | ||
| 149 | return "Texture1DArray"; | ||
| 150 | case SurfaceTarget::Texture2DArray: | ||
| 151 | return "Texture2DArray"; | ||
| 152 | case SurfaceTarget::TextureCubemap: | ||
| 153 | return "TextureCubemap"; | ||
| 154 | default: | ||
| 155 | LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", static_cast<u32>(target)); | ||
| 156 | UNREACHABLE(); | ||
| 157 | return fmt::format("TextureUnknown({})", static_cast<u32>(target)); | ||
| 158 | } | ||
| 159 | } | ||
| 160 | |||
| 139 | /** | 161 | /** |
| 140 | * Gets the compression factor for the specified PixelFormat. This applies to just the | 162 | * Gets the compression factor for the specified PixelFormat. This applies to just the |
| 141 | * "compressed width" and "compressed height", not the overall compression factor of a | 163 | * "compressed width" and "compressed height", not the overall compression factor of a |
| @@ -192,6 +214,7 @@ struct SurfaceParams { | |||
| 192 | 1, // RG8S | 214 | 1, // RG8S |
| 193 | 1, // RG32UI | 215 | 1, // RG32UI |
| 194 | 1, // R32UI | 216 | 1, // R32UI |
| 217 | 4, // ASTC_2D_8X8 | ||
| 195 | 1, // Z32F | 218 | 1, // Z32F |
| 196 | 1, // Z16 | 219 | 1, // Z16 |
| 197 | 1, // Z24S8 | 220 | 1, // Z24S8 |
| @@ -199,8 +222,8 @@ struct SurfaceParams { | |||
| 199 | 1, // Z32FS8 | 222 | 1, // Z32FS8 |
| 200 | }}; | 223 | }}; |
| 201 | 224 | ||
| 202 | ASSERT(static_cast<size_t>(format) < compression_factor_table.size()); | 225 | ASSERT(static_cast<std::size_t>(format) < compression_factor_table.size()); |
| 203 | return compression_factor_table[static_cast<size_t>(format)]; | 226 | return compression_factor_table[static_cast<std::size_t>(format)]; |
| 204 | } | 227 | } |
| 205 | 228 | ||
| 206 | static constexpr u32 GetFormatBpp(PixelFormat format) { | 229 | static constexpr u32 GetFormatBpp(PixelFormat format) { |
| @@ -253,6 +276,7 @@ struct SurfaceParams { | |||
| 253 | 16, // RG8S | 276 | 16, // RG8S |
| 254 | 64, // RG32UI | 277 | 64, // RG32UI |
| 255 | 32, // R32UI | 278 | 32, // R32UI |
| 279 | 16, // ASTC_2D_8X8 | ||
| 256 | 32, // Z32F | 280 | 32, // Z32F |
| 257 | 16, // Z16 | 281 | 16, // Z16 |
| 258 | 32, // Z24S8 | 282 | 32, // Z24S8 |
| @@ -260,8 +284,8 @@ struct SurfaceParams { | |||
| 260 | 64, // Z32FS8 | 284 | 64, // Z32FS8 |
| 261 | }}; | 285 | }}; |
| 262 | 286 | ||
| 263 | ASSERT(static_cast<size_t>(format) < bpp_table.size()); | 287 | ASSERT(static_cast<std::size_t>(format) < bpp_table.size()); |
| 264 | return bpp_table[static_cast<size_t>(format)]; | 288 | return bpp_table[static_cast<std::size_t>(format)]; |
| 265 | } | 289 | } |
| 266 | 290 | ||
| 267 | u32 GetFormatBpp() const { | 291 | u32 GetFormatBpp() const { |
| @@ -316,6 +340,8 @@ struct SurfaceParams { | |||
| 316 | return PixelFormat::R11FG11FB10F; | 340 | return PixelFormat::R11FG11FB10F; |
| 317 | case Tegra::RenderTargetFormat::B5G6R5_UNORM: | 341 | case Tegra::RenderTargetFormat::B5G6R5_UNORM: |
| 318 | return PixelFormat::B5G6R5U; | 342 | return PixelFormat::B5G6R5U; |
| 343 | case Tegra::RenderTargetFormat::BGR5A1_UNORM: | ||
| 344 | return PixelFormat::A1B5G5R5U; | ||
| 319 | case Tegra::RenderTargetFormat::RGBA32_UINT: | 345 | case Tegra::RenderTargetFormat::RGBA32_UINT: |
| 320 | return PixelFormat::RGBA32UI; | 346 | return PixelFormat::RGBA32UI; |
| 321 | case Tegra::RenderTargetFormat::R8_UNORM: | 347 | case Tegra::RenderTargetFormat::R8_UNORM: |
| @@ -522,6 +548,8 @@ struct SurfaceParams { | |||
| 522 | return PixelFormat::BC6H_SF16; | 548 | return PixelFormat::BC6H_SF16; |
| 523 | case Tegra::Texture::TextureFormat::ASTC_2D_4X4: | 549 | case Tegra::Texture::TextureFormat::ASTC_2D_4X4: |
| 524 | return PixelFormat::ASTC_2D_4X4; | 550 | return PixelFormat::ASTC_2D_4X4; |
| 551 | case Tegra::Texture::TextureFormat::ASTC_2D_8X8: | ||
| 552 | return PixelFormat::ASTC_2D_8X8; | ||
| 525 | case Tegra::Texture::TextureFormat::R16_G16: | 553 | case Tegra::Texture::TextureFormat::R16_G16: |
| 526 | switch (component_type) { | 554 | switch (component_type) { |
| 527 | case Tegra::Texture::ComponentType::FLOAT: | 555 | case Tegra::Texture::ComponentType::FLOAT: |
| @@ -576,6 +604,7 @@ struct SurfaceParams { | |||
| 576 | case Tegra::RenderTargetFormat::RG16_UNORM: | 604 | case Tegra::RenderTargetFormat::RG16_UNORM: |
| 577 | case Tegra::RenderTargetFormat::R16_UNORM: | 605 | case Tegra::RenderTargetFormat::R16_UNORM: |
| 578 | case Tegra::RenderTargetFormat::B5G6R5_UNORM: | 606 | case Tegra::RenderTargetFormat::B5G6R5_UNORM: |
| 607 | case Tegra::RenderTargetFormat::BGR5A1_UNORM: | ||
| 579 | case Tegra::RenderTargetFormat::RG8_UNORM: | 608 | case Tegra::RenderTargetFormat::RG8_UNORM: |
| 580 | case Tegra::RenderTargetFormat::RGBA16_UNORM: | 609 | case Tegra::RenderTargetFormat::RGBA16_UNORM: |
| 581 | return ComponentType::UNorm; | 610 | return ComponentType::UNorm; |
| @@ -636,16 +665,18 @@ struct SurfaceParams { | |||
| 636 | } | 665 | } |
| 637 | 666 | ||
| 638 | static SurfaceType GetFormatType(PixelFormat pixel_format) { | 667 | static SurfaceType GetFormatType(PixelFormat pixel_format) { |
| 639 | if (static_cast<size_t>(pixel_format) < static_cast<size_t>(PixelFormat::MaxColorFormat)) { | 668 | if (static_cast<std::size_t>(pixel_format) < |
| 669 | static_cast<std::size_t>(PixelFormat::MaxColorFormat)) { | ||
| 640 | return SurfaceType::ColorTexture; | 670 | return SurfaceType::ColorTexture; |
| 641 | } | 671 | } |
| 642 | 672 | ||
| 643 | if (static_cast<size_t>(pixel_format) < static_cast<size_t>(PixelFormat::MaxDepthFormat)) { | 673 | if (static_cast<std::size_t>(pixel_format) < |
| 674 | static_cast<std::size_t>(PixelFormat::MaxDepthFormat)) { | ||
| 644 | return SurfaceType::Depth; | 675 | return SurfaceType::Depth; |
| 645 | } | 676 | } |
| 646 | 677 | ||
| 647 | if (static_cast<size_t>(pixel_format) < | 678 | if (static_cast<std::size_t>(pixel_format) < |
| 648 | static_cast<size_t>(PixelFormat::MaxDepthStencilFormat)) { | 679 | static_cast<std::size_t>(PixelFormat::MaxDepthStencilFormat)) { |
| 649 | return SurfaceType::DepthStencil; | 680 | return SurfaceType::DepthStencil; |
| 650 | } | 681 | } |
| 651 | 682 | ||
| @@ -659,7 +690,7 @@ struct SurfaceParams { | |||
| 659 | MathUtil::Rectangle<u32> GetRect() const; | 690 | MathUtil::Rectangle<u32> GetRect() const; |
| 660 | 691 | ||
| 661 | /// Returns the size of this surface in bytes, adjusted for compression | 692 | /// Returns the size of this surface in bytes, adjusted for compression |
| 662 | size_t SizeInBytes() const { | 693 | std::size_t SizeInBytes() const { |
| 663 | const u32 compression_factor{GetCompressionFactor(pixel_format)}; | 694 | const u32 compression_factor{GetCompressionFactor(pixel_format)}; |
| 664 | ASSERT(width % compression_factor == 0); | 695 | ASSERT(width % compression_factor == 0); |
| 665 | ASSERT(height % compression_factor == 0); | 696 | ASSERT(height % compression_factor == 0); |
| @@ -671,7 +702,7 @@ struct SurfaceParams { | |||
| 671 | static SurfaceParams CreateForTexture(const Tegra::Texture::FullTextureInfo& config); | 702 | static SurfaceParams CreateForTexture(const Tegra::Texture::FullTextureInfo& config); |
| 672 | 703 | ||
| 673 | /// Creates SurfaceParams from a framebuffer configuration | 704 | /// Creates SurfaceParams from a framebuffer configuration |
| 674 | static SurfaceParams CreateForFramebuffer(size_t index); | 705 | static SurfaceParams CreateForFramebuffer(std::size_t index); |
| 675 | 706 | ||
| 676 | /// Creates SurfaceParams for a depth buffer configuration | 707 | /// Creates SurfaceParams for a depth buffer configuration |
| 677 | static SurfaceParams CreateForDepthBuffer(u32 zeta_width, u32 zeta_height, | 708 | static SurfaceParams CreateForDepthBuffer(u32 zeta_width, u32 zeta_height, |
| @@ -694,7 +725,7 @@ struct SurfaceParams { | |||
| 694 | u32 height; | 725 | u32 height; |
| 695 | u32 depth; | 726 | u32 depth; |
| 696 | u32 unaligned_height; | 727 | u32 unaligned_height; |
| 697 | size_t size_in_bytes; | 728 | std::size_t size_in_bytes; |
| 698 | SurfaceTarget target; | 729 | SurfaceTarget target; |
| 699 | }; | 730 | }; |
| 700 | 731 | ||
| @@ -711,7 +742,7 @@ struct SurfaceReserveKey : Common::HashableStruct<OpenGL::SurfaceParams> { | |||
| 711 | namespace std { | 742 | namespace std { |
| 712 | template <> | 743 | template <> |
| 713 | struct hash<SurfaceReserveKey> { | 744 | struct hash<SurfaceReserveKey> { |
| 714 | size_t operator()(const SurfaceReserveKey& k) const { | 745 | std::size_t operator()(const SurfaceReserveKey& k) const { |
| 715 | return k.Hash(); | 746 | return k.Hash(); |
| 716 | } | 747 | } |
| 717 | }; | 748 | }; |
| @@ -727,7 +758,7 @@ public: | |||
| 727 | return params.addr; | 758 | return params.addr; |
| 728 | } | 759 | } |
| 729 | 760 | ||
| 730 | size_t GetSizeInBytes() const { | 761 | std::size_t GetSizeInBytes() const { |
| 731 | return params.size_in_bytes; | 762 | return params.size_in_bytes; |
| 732 | } | 763 | } |
| 733 | 764 | ||
| @@ -775,7 +806,7 @@ public: | |||
| 775 | Surface GetDepthBufferSurface(bool preserve_contents); | 806 | Surface GetDepthBufferSurface(bool preserve_contents); |
| 776 | 807 | ||
| 777 | /// Get the color surface based on the framebuffer configuration and the specified render target | 808 | /// Get the color surface based on the framebuffer configuration and the specified render target |
| 778 | Surface GetColorBufferSurface(size_t index, bool preserve_contents); | 809 | Surface GetColorBufferSurface(std::size_t index, bool preserve_contents); |
| 779 | 810 | ||
| 780 | /// Flushes the surface to Switch memory | 811 | /// Flushes the surface to Switch memory |
| 781 | void FlushSurface(const Surface& surface); | 812 | void FlushSurface(const Surface& surface); |
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 61080f5cc..7cd8f91e4 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp | |||
| @@ -8,13 +8,14 @@ | |||
| 8 | #include "video_core/engines/maxwell_3d.h" | 8 | #include "video_core/engines/maxwell_3d.h" |
| 9 | #include "video_core/renderer_opengl/gl_shader_cache.h" | 9 | #include "video_core/renderer_opengl/gl_shader_cache.h" |
| 10 | #include "video_core/renderer_opengl/gl_shader_manager.h" | 10 | #include "video_core/renderer_opengl/gl_shader_manager.h" |
| 11 | #include "video_core/utils.h" | ||
| 11 | 12 | ||
| 12 | namespace OpenGL { | 13 | namespace OpenGL { |
| 13 | 14 | ||
| 14 | /// Gets the address for the specified shader stage program | 15 | /// Gets the address for the specified shader stage program |
| 15 | static VAddr GetShaderAddress(Maxwell::ShaderProgram program) { | 16 | static VAddr GetShaderAddress(Maxwell::ShaderProgram program) { |
| 16 | const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); | 17 | const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); |
| 17 | const auto& shader_config = gpu.regs.shader_config[static_cast<size_t>(program)]; | 18 | const auto& shader_config = gpu.regs.shader_config[static_cast<std::size_t>(program)]; |
| 18 | return *gpu.memory_manager.GpuToCpuAddress(gpu.regs.code_address.CodeAddress() + | 19 | return *gpu.memory_manager.GpuToCpuAddress(gpu.regs.code_address.CodeAddress() + |
| 19 | shader_config.offset); | 20 | shader_config.offset); |
| 20 | } | 21 | } |
| @@ -28,7 +29,7 @@ static GLShader::ProgramCode GetShaderCode(VAddr addr) { | |||
| 28 | 29 | ||
| 29 | /// Helper function to set shader uniform block bindings for a single shader stage | 30 | /// Helper function to set shader uniform block bindings for a single shader stage |
| 30 | static void SetShaderUniformBlockBinding(GLuint shader, const char* name, | 31 | static void SetShaderUniformBlockBinding(GLuint shader, const char* name, |
| 31 | Maxwell::ShaderStage binding, size_t expected_size) { | 32 | Maxwell::ShaderStage binding, std::size_t expected_size) { |
| 32 | const GLuint ub_index = glGetUniformBlockIndex(shader, name); | 33 | const GLuint ub_index = glGetUniformBlockIndex(shader, name); |
| 33 | if (ub_index == GL_INVALID_INDEX) { | 34 | if (ub_index == GL_INVALID_INDEX) { |
| 34 | return; | 35 | return; |
| @@ -36,7 +37,7 @@ static void SetShaderUniformBlockBinding(GLuint shader, const char* name, | |||
| 36 | 37 | ||
| 37 | GLint ub_size = 0; | 38 | GLint ub_size = 0; |
| 38 | glGetActiveUniformBlockiv(shader, ub_index, GL_UNIFORM_BLOCK_DATA_SIZE, &ub_size); | 39 | glGetActiveUniformBlockiv(shader, ub_index, GL_UNIFORM_BLOCK_DATA_SIZE, &ub_size); |
| 39 | ASSERT_MSG(static_cast<size_t>(ub_size) == expected_size, | 40 | ASSERT_MSG(static_cast<std::size_t>(ub_size) == expected_size, |
| 40 | "Uniform block size did not match! Got {}, expected {}", ub_size, expected_size); | 41 | "Uniform block size did not match! Got {}, expected {}", ub_size, expected_size); |
| 41 | glUniformBlockBinding(shader, ub_index, static_cast<GLuint>(binding)); | 42 | glUniformBlockBinding(shader, ub_index, static_cast<GLuint>(binding)); |
| 42 | } | 43 | } |
| @@ -83,6 +84,7 @@ CachedShader::CachedShader(VAddr addr, Maxwell::ShaderProgram program_type) | |||
| 83 | shader.Create(program_result.first.c_str(), gl_type); | 84 | shader.Create(program_result.first.c_str(), gl_type); |
| 84 | program.Create(true, shader.handle); | 85 | program.Create(true, shader.handle); |
| 85 | SetShaderUniformBlockBindings(program.handle); | 86 | SetShaderUniformBlockBindings(program.handle); |
| 87 | VideoCore::LabelGLObject(GL_PROGRAM, program.handle, addr); | ||
| 86 | } | 88 | } |
| 87 | 89 | ||
| 88 | GLuint CachedShader::GetProgramResourceIndex(const GLShader::ConstBufferEntry& buffer) { | 90 | GLuint CachedShader::GetProgramResourceIndex(const GLShader::ConstBufferEntry& buffer) { |
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h index 6e6febcbc..9bafe43a9 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_cache.h | |||
| @@ -28,7 +28,7 @@ public: | |||
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | /// Gets the size of the shader in guest memory, required for cache management | 30 | /// Gets the size of the shader in guest memory, required for cache management |
| 31 | size_t GetSizeInBytes() const { | 31 | std::size_t GetSizeInBytes() const { |
| 32 | return GLShader::MAX_PROGRAM_CODE_LENGTH * sizeof(u64); | 32 | return GLShader::MAX_PROGRAM_CODE_LENGTH * sizeof(u64); |
| 33 | } | 33 | } |
| 34 | 34 | ||
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 2d56370c7..b3e95187e 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | #include "common/assert.h" | 12 | #include "common/assert.h" |
| 13 | #include "common/common_types.h" | 13 | #include "common/common_types.h" |
| 14 | #include "video_core/engines/shader_bytecode.h" | 14 | #include "video_core/engines/shader_bytecode.h" |
| 15 | #include "video_core/engines/shader_header.h" | ||
| 15 | #include "video_core/renderer_opengl/gl_rasterizer.h" | 16 | #include "video_core/renderer_opengl/gl_rasterizer.h" |
| 16 | #include "video_core/renderer_opengl/gl_shader_decompiler.h" | 17 | #include "video_core/renderer_opengl/gl_shader_decompiler.h" |
| 17 | 18 | ||
| @@ -26,7 +27,7 @@ using Tegra::Shader::Sampler; | |||
| 26 | using Tegra::Shader::SubOp; | 27 | using Tegra::Shader::SubOp; |
| 27 | 28 | ||
| 28 | constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH; | 29 | constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH; |
| 29 | constexpr u32 PROGRAM_HEADER_SIZE = 0x50; | 30 | constexpr u32 PROGRAM_HEADER_SIZE = sizeof(Tegra::Shader::Header); |
| 30 | 31 | ||
| 31 | class DecompileFail : public std::runtime_error { | 32 | class DecompileFail : public std::runtime_error { |
| 32 | public: | 33 | public: |
| @@ -189,7 +190,7 @@ public: | |||
| 189 | 190 | ||
| 190 | private: | 191 | private: |
| 191 | void AppendIndentation() { | 192 | void AppendIndentation() { |
| 192 | shader_source.append(static_cast<size_t>(scope) * 4, ' '); | 193 | shader_source.append(static_cast<std::size_t>(scope) * 4, ' '); |
| 193 | } | 194 | } |
| 194 | 195 | ||
| 195 | std::string shader_source; | 196 | std::string shader_source; |
| @@ -208,7 +209,7 @@ public: | |||
| 208 | UnsignedInteger, | 209 | UnsignedInteger, |
| 209 | }; | 210 | }; |
| 210 | 211 | ||
| 211 | GLSLRegister(size_t index, const std::string& suffix) : index{index}, suffix{suffix} {} | 212 | GLSLRegister(std::size_t index, const std::string& suffix) : index{index}, suffix{suffix} {} |
| 212 | 213 | ||
| 213 | /// Gets the GLSL type string for a register | 214 | /// Gets the GLSL type string for a register |
| 214 | static std::string GetTypeString() { | 215 | static std::string GetTypeString() { |
| @@ -226,15 +227,23 @@ public: | |||
| 226 | } | 227 | } |
| 227 | 228 | ||
| 228 | /// Returns the index of the register | 229 | /// Returns the index of the register |
| 229 | size_t GetIndex() const { | 230 | std::size_t GetIndex() const { |
| 230 | return index; | 231 | return index; |
| 231 | } | 232 | } |
| 232 | 233 | ||
| 233 | private: | 234 | private: |
| 234 | const size_t index; | 235 | const std::size_t index; |
| 235 | const std::string& suffix; | 236 | const std::string& suffix; |
| 236 | }; | 237 | }; |
| 237 | 238 | ||
| 239 | enum class InternalFlag : u64 { | ||
| 240 | ZeroFlag = 0, | ||
| 241 | CarryFlag = 1, | ||
| 242 | OverflowFlag = 2, | ||
| 243 | NaNFlag = 3, | ||
| 244 | Amount | ||
| 245 | }; | ||
| 246 | |||
| 238 | /** | 247 | /** |
| 239 | * Used to manage shader registers that are emulated with GLSL. This class keeps track of the state | 248 | * Used to manage shader registers that are emulated with GLSL. This class keeps track of the state |
| 240 | * of all registers (e.g. whether they are currently being used as Floats or Integers), and | 249 | * of all registers (e.g. whether they are currently being used as Floats or Integers), and |
| @@ -328,13 +337,19 @@ public: | |||
| 328 | void SetRegisterToInteger(const Register& reg, bool is_signed, u64 elem, | 337 | void SetRegisterToInteger(const Register& reg, bool is_signed, u64 elem, |
| 329 | const std::string& value, u64 dest_num_components, | 338 | const std::string& value, u64 dest_num_components, |
| 330 | u64 value_num_components, bool is_saturated = false, | 339 | u64 value_num_components, bool is_saturated = false, |
| 331 | u64 dest_elem = 0, Register::Size size = Register::Size::Word) { | 340 | u64 dest_elem = 0, Register::Size size = Register::Size::Word, |
| 341 | bool sets_cc = false) { | ||
| 332 | ASSERT_MSG(!is_saturated, "Unimplemented"); | 342 | ASSERT_MSG(!is_saturated, "Unimplemented"); |
| 333 | 343 | ||
| 334 | const std::string func{is_signed ? "intBitsToFloat" : "uintBitsToFloat"}; | 344 | const std::string func{is_signed ? "intBitsToFloat" : "uintBitsToFloat"}; |
| 335 | 345 | ||
| 336 | SetRegister(reg, elem, func + '(' + ConvertIntegerSize(value, size) + ')', | 346 | SetRegister(reg, elem, func + '(' + ConvertIntegerSize(value, size) + ')', |
| 337 | dest_num_components, value_num_components, dest_elem); | 347 | dest_num_components, value_num_components, dest_elem); |
| 348 | |||
| 349 | if (sets_cc) { | ||
| 350 | const std::string zero_condition = "( " + ConvertIntegerSize(value, size) + " == 0 )"; | ||
| 351 | SetInternalFlag(InternalFlag::ZeroFlag, zero_condition); | ||
| 352 | } | ||
| 338 | } | 353 | } |
| 339 | 354 | ||
| 340 | /** | 355 | /** |
| @@ -351,6 +366,26 @@ public: | |||
| 351 | shader.AddLine(dest + " = " + src + ';'); | 366 | shader.AddLine(dest + " = " + src + ';'); |
| 352 | } | 367 | } |
| 353 | 368 | ||
| 369 | std::string GetControlCode(const Tegra::Shader::ControlCode cc) const { | ||
| 370 | switch (cc) { | ||
| 371 | case Tegra::Shader::ControlCode::NEU: | ||
| 372 | return "!(" + GetInternalFlag(InternalFlag::ZeroFlag) + ')'; | ||
| 373 | default: | ||
| 374 | LOG_CRITICAL(HW_GPU, "Unimplemented Control Code {}", static_cast<u32>(cc)); | ||
| 375 | UNREACHABLE(); | ||
| 376 | return "false"; | ||
| 377 | } | ||
| 378 | } | ||
| 379 | |||
| 380 | std::string GetInternalFlag(const InternalFlag ii) const { | ||
| 381 | const u32 code = static_cast<u32>(ii); | ||
| 382 | return "internalFlag_" + std::to_string(code) + suffix; | ||
| 383 | } | ||
| 384 | |||
| 385 | void SetInternalFlag(const InternalFlag ii, const std::string& value) const { | ||
| 386 | shader.AddLine(GetInternalFlag(ii) + " = " + value + ';'); | ||
| 387 | } | ||
| 388 | |||
| 354 | /** | 389 | /** |
| 355 | * Writes code that does a output attribute assignment to register operation. Output attributes | 390 | * Writes code that does a output attribute assignment to register operation. Output attributes |
| 356 | * are stored as floats, so this may require conversion. | 391 | * are stored as floats, so this may require conversion. |
| @@ -414,6 +449,12 @@ public: | |||
| 414 | } | 449 | } |
| 415 | declarations.AddNewLine(); | 450 | declarations.AddNewLine(); |
| 416 | 451 | ||
| 452 | for (u32 ii = 0; ii < static_cast<u64>(InternalFlag::Amount); ii++) { | ||
| 453 | const InternalFlag code = static_cast<InternalFlag>(ii); | ||
| 454 | declarations.AddLine("bool " + GetInternalFlag(code) + " = false;"); | ||
| 455 | } | ||
| 456 | declarations.AddNewLine(); | ||
| 457 | |||
| 417 | for (const auto element : declr_input_attribute) { | 458 | for (const auto element : declr_input_attribute) { |
| 418 | // TODO(bunnei): Use proper number of elements for these | 459 | // TODO(bunnei): Use proper number of elements for these |
| 419 | u32 idx = | 460 | u32 idx = |
| @@ -468,7 +509,7 @@ public: | |||
| 468 | /// necessary. | 509 | /// necessary. |
| 469 | std::string AccessSampler(const Sampler& sampler, Tegra::Shader::TextureType type, | 510 | std::string AccessSampler(const Sampler& sampler, Tegra::Shader::TextureType type, |
| 470 | bool is_array) { | 511 | bool is_array) { |
| 471 | const size_t offset = static_cast<size_t>(sampler.index.Value()); | 512 | const std::size_t offset = static_cast<std::size_t>(sampler.index.Value()); |
| 472 | 513 | ||
| 473 | // If this sampler has already been used, return the existing mapping. | 514 | // If this sampler has already been used, return the existing mapping. |
| 474 | const auto itr = | 515 | const auto itr = |
| @@ -481,7 +522,7 @@ public: | |||
| 481 | } | 522 | } |
| 482 | 523 | ||
| 483 | // Otherwise create a new mapping for this sampler | 524 | // Otherwise create a new mapping for this sampler |
| 484 | const size_t next_index = used_samplers.size(); | 525 | const std::size_t next_index = used_samplers.size(); |
| 485 | const SamplerEntry entry{stage, offset, next_index, type, is_array}; | 526 | const SamplerEntry entry{stage, offset, next_index, type, is_array}; |
| 486 | used_samplers.emplace_back(entry); | 527 | used_samplers.emplace_back(entry); |
| 487 | return entry.GetName(); | 528 | return entry.GetName(); |
| @@ -531,7 +572,7 @@ private: | |||
| 531 | void BuildRegisterList() { | 572 | void BuildRegisterList() { |
| 532 | regs.reserve(Register::NumRegisters); | 573 | regs.reserve(Register::NumRegisters); |
| 533 | 574 | ||
| 534 | for (size_t index = 0; index < Register::NumRegisters; ++index) { | 575 | for (std::size_t index = 0; index < Register::NumRegisters; ++index) { |
| 535 | regs.emplace_back(index, suffix); | 576 | regs.emplace_back(index, suffix); |
| 536 | } | 577 | } |
| 537 | } | 578 | } |
| @@ -674,7 +715,7 @@ public: | |||
| 674 | u32 main_offset, Maxwell3D::Regs::ShaderStage stage, const std::string& suffix) | 715 | u32 main_offset, Maxwell3D::Regs::ShaderStage stage, const std::string& suffix) |
| 675 | : subroutines(subroutines), program_code(program_code), main_offset(main_offset), | 716 | : subroutines(subroutines), program_code(program_code), main_offset(main_offset), |
| 676 | stage(stage), suffix(suffix) { | 717 | stage(stage), suffix(suffix) { |
| 677 | 718 | std::memcpy(&header, program_code.data(), sizeof(Tegra::Shader::Header)); | |
| 678 | Generate(suffix); | 719 | Generate(suffix); |
| 679 | } | 720 | } |
| 680 | 721 | ||
| @@ -688,23 +729,6 @@ public: | |||
| 688 | } | 729 | } |
| 689 | 730 | ||
| 690 | private: | 731 | private: |
| 691 | // Shader program header for a Fragment Shader. | ||
| 692 | struct FragmentHeader { | ||
| 693 | INSERT_PADDING_WORDS(5); | ||
| 694 | INSERT_PADDING_WORDS(13); | ||
| 695 | u32 enabled_color_outputs; | ||
| 696 | union { | ||
| 697 | BitField<0, 1, u32> writes_samplemask; | ||
| 698 | BitField<1, 1, u32> writes_depth; | ||
| 699 | }; | ||
| 700 | |||
| 701 | bool IsColorComponentOutputEnabled(u32 render_target, u32 component) const { | ||
| 702 | const u32 bit = render_target * 4 + component; | ||
| 703 | return enabled_color_outputs & (1 << bit); | ||
| 704 | } | ||
| 705 | }; | ||
| 706 | static_assert(sizeof(FragmentHeader) == PROGRAM_HEADER_SIZE, "FragmentHeader size is wrong"); | ||
| 707 | |||
| 708 | /// Gets the Subroutine object corresponding to the specified address. | 732 | /// Gets the Subroutine object corresponding to the specified address. |
| 709 | const Subroutine& GetSubroutine(u32 begin, u32 end) const { | 733 | const Subroutine& GetSubroutine(u32 begin, u32 end) const { |
| 710 | const auto iter = subroutines.find(Subroutine{begin, end, suffix}); | 734 | const auto iter = subroutines.find(Subroutine{begin, end, suffix}); |
| @@ -862,7 +886,7 @@ private: | |||
| 862 | */ | 886 | */ |
| 863 | bool IsSchedInstruction(u32 offset) const { | 887 | bool IsSchedInstruction(u32 offset) const { |
| 864 | // sched instructions appear once every 4 instructions. | 888 | // sched instructions appear once every 4 instructions. |
| 865 | static constexpr size_t SchedPeriod = 4; | 889 | static constexpr std::size_t SchedPeriod = 4; |
| 866 | u32 absolute_offset = offset - main_offset; | 890 | u32 absolute_offset = offset - main_offset; |
| 867 | 891 | ||
| 868 | return (absolute_offset % SchedPeriod) == 0; | 892 | return (absolute_offset % SchedPeriod) == 0; |
| @@ -930,7 +954,7 @@ private: | |||
| 930 | std::string result; | 954 | std::string result; |
| 931 | result += '('; | 955 | result += '('; |
| 932 | 956 | ||
| 933 | for (size_t i = 0; i < shift_amounts.size(); ++i) { | 957 | for (std::size_t i = 0; i < shift_amounts.size(); ++i) { |
| 934 | if (i) | 958 | if (i) |
| 935 | result += '|'; | 959 | result += '|'; |
| 936 | result += "(((" + imm_lut + " >> (((" + op_c + " >> " + shift_amounts[i] + | 960 | result += "(((" + imm_lut + " >> (((" + op_c + " >> " + shift_amounts[i] + |
| @@ -954,9 +978,7 @@ private: | |||
| 954 | // TEXS has two destination registers and a swizzle. The first two elements in the swizzle | 978 | // TEXS has two destination registers and a swizzle. The first two elements in the swizzle |
| 955 | // go into gpr0+0 and gpr0+1, and the rest goes into gpr28+0 and gpr28+1 | 979 | // go into gpr0+0 and gpr0+1, and the rest goes into gpr28+0 and gpr28+1 |
| 956 | 980 | ||
| 957 | ASSERT_MSG(instr.texs.nodep == 0, "TEXS nodep not implemented"); | 981 | std::size_t written_components = 0; |
| 958 | |||
| 959 | size_t written_components = 0; | ||
| 960 | for (u32 component = 0; component < 4; ++component) { | 982 | for (u32 component = 0; component < 4; ++component) { |
| 961 | if (!instr.texs.IsComponentEnabled(component)) { | 983 | if (!instr.texs.IsComponentEnabled(component)) { |
| 962 | continue; | 984 | continue; |
| @@ -1010,10 +1032,8 @@ private: | |||
| 1010 | /// Writes the output values from a fragment shader to the corresponding GLSL output variables. | 1032 | /// Writes the output values from a fragment shader to the corresponding GLSL output variables. |
| 1011 | void EmitFragmentOutputsWrite() { | 1033 | void EmitFragmentOutputsWrite() { |
| 1012 | ASSERT(stage == Maxwell3D::Regs::ShaderStage::Fragment); | 1034 | ASSERT(stage == Maxwell3D::Regs::ShaderStage::Fragment); |
| 1013 | FragmentHeader header; | ||
| 1014 | std::memcpy(&header, program_code.data(), PROGRAM_HEADER_SIZE); | ||
| 1015 | 1035 | ||
| 1016 | ASSERT_MSG(header.writes_samplemask == 0, "Samplemask write is unimplemented"); | 1036 | ASSERT_MSG(header.ps.omap.sample_mask == 0, "Samplemask write is unimplemented"); |
| 1017 | 1037 | ||
| 1018 | // Write the color outputs using the data in the shader registers, disabled | 1038 | // Write the color outputs using the data in the shader registers, disabled |
| 1019 | // rendertargets/components are skipped in the register assignment. | 1039 | // rendertargets/components are skipped in the register assignment. |
| @@ -1022,7 +1042,7 @@ private: | |||
| 1022 | ++render_target) { | 1042 | ++render_target) { |
| 1023 | // TODO(Subv): Figure out how dual-source blending is configured in the Switch. | 1043 | // TODO(Subv): Figure out how dual-source blending is configured in the Switch. |
| 1024 | for (u32 component = 0; component < 4; ++component) { | 1044 | for (u32 component = 0; component < 4; ++component) { |
| 1025 | if (header.IsColorComponentOutputEnabled(render_target, component)) { | 1045 | if (header.ps.IsColorComponentOutputEnabled(render_target, component)) { |
| 1026 | shader.AddLine(fmt::format("FragColor{}[{}] = {};", render_target, component, | 1046 | shader.AddLine(fmt::format("FragColor{}[{}] = {};", render_target, component, |
| 1027 | regs.GetRegisterAsFloat(current_reg))); | 1047 | regs.GetRegisterAsFloat(current_reg))); |
| 1028 | ++current_reg; | 1048 | ++current_reg; |
| @@ -1030,7 +1050,7 @@ private: | |||
| 1030 | } | 1050 | } |
| 1031 | } | 1051 | } |
| 1032 | 1052 | ||
| 1033 | if (header.writes_depth) { | 1053 | if (header.ps.omap.depth) { |
| 1034 | // The depth output is always 2 registers after the last color output, and current_reg | 1054 | // The depth output is always 2 registers after the last color output, and current_reg |
| 1035 | // already contains one past the last color register. | 1055 | // already contains one past the last color register. |
| 1036 | 1056 | ||
| @@ -1510,8 +1530,6 @@ private: | |||
| 1510 | case OpCode::Id::LEA_IMM: | 1530 | case OpCode::Id::LEA_IMM: |
| 1511 | case OpCode::Id::LEA_RZ: | 1531 | case OpCode::Id::LEA_RZ: |
| 1512 | case OpCode::Id::LEA_HI: { | 1532 | case OpCode::Id::LEA_HI: { |
| 1513 | std::string op_a; | ||
| 1514 | std::string op_b; | ||
| 1515 | std::string op_c; | 1533 | std::string op_c; |
| 1516 | 1534 | ||
| 1517 | switch (opcode->GetId()) { | 1535 | switch (opcode->GetId()) { |
| @@ -1642,7 +1660,8 @@ private: | |||
| 1642 | } | 1660 | } |
| 1643 | 1661 | ||
| 1644 | regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1, | 1662 | regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1, |
| 1645 | 1, instr.alu.saturate_d, 0, instr.conversion.dest_size); | 1663 | 1, instr.alu.saturate_d, 0, instr.conversion.dest_size, |
| 1664 | instr.generates_cc.Value() != 0); | ||
| 1646 | break; | 1665 | break; |
| 1647 | } | 1666 | } |
| 1648 | case OpCode::Id::I2F_R: | 1667 | case OpCode::Id::I2F_R: |
| @@ -1772,13 +1791,34 @@ private: | |||
| 1772 | case OpCode::Type::Memory: { | 1791 | case OpCode::Type::Memory: { |
| 1773 | switch (opcode->GetId()) { | 1792 | switch (opcode->GetId()) { |
| 1774 | case OpCode::Id::LD_A: { | 1793 | case OpCode::Id::LD_A: { |
| 1775 | ASSERT_MSG(instr.attribute.fmt20.size == 0, "untested"); | ||
| 1776 | // Note: Shouldn't this be interp mode flat? As in no interpolation made. | 1794 | // Note: Shouldn't this be interp mode flat? As in no interpolation made. |
| 1795 | ASSERT_MSG(instr.gpr8.Value() == Register::ZeroIndex, | ||
| 1796 | "Indirect attribute loads are not supported"); | ||
| 1797 | ASSERT_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) == 0, | ||
| 1798 | "Unaligned attribute loads are not supported"); | ||
| 1777 | 1799 | ||
| 1778 | Tegra::Shader::IpaMode input_mode{Tegra::Shader::IpaInterpMode::Perspective, | 1800 | Tegra::Shader::IpaMode input_mode{Tegra::Shader::IpaInterpMode::Perspective, |
| 1779 | Tegra::Shader::IpaSampleMode::Default}; | 1801 | Tegra::Shader::IpaSampleMode::Default}; |
| 1780 | regs.SetRegisterToInputAttibute(instr.gpr0, instr.attribute.fmt20.element, | 1802 | |
| 1781 | instr.attribute.fmt20.index, input_mode); | 1803 | u64 next_element = instr.attribute.fmt20.element; |
| 1804 | u64 next_index = static_cast<u64>(instr.attribute.fmt20.index.Value()); | ||
| 1805 | |||
| 1806 | const auto LoadNextElement = [&](u32 reg_offset) { | ||
| 1807 | regs.SetRegisterToInputAttibute(instr.gpr0.Value() + reg_offset, next_element, | ||
| 1808 | static_cast<Attribute::Index>(next_index), | ||
| 1809 | input_mode); | ||
| 1810 | |||
| 1811 | // Load the next attribute element into the following register. If the element | ||
| 1812 | // to load goes beyond the vec4 size, load the first element of the next | ||
| 1813 | // attribute. | ||
| 1814 | next_element = (next_element + 1) % 4; | ||
| 1815 | next_index = next_index + (next_element == 0 ? 1 : 0); | ||
| 1816 | }; | ||
| 1817 | |||
| 1818 | const u32 num_words = static_cast<u32>(instr.attribute.fmt20.size.Value()) + 1; | ||
| 1819 | for (u32 reg_offset = 0; reg_offset < num_words; ++reg_offset) { | ||
| 1820 | LoadNextElement(reg_offset); | ||
| 1821 | } | ||
| 1782 | break; | 1822 | break; |
| 1783 | } | 1823 | } |
| 1784 | case OpCode::Id::LD_C: { | 1824 | case OpCode::Id::LD_C: { |
| @@ -1820,9 +1860,31 @@ private: | |||
| 1820 | break; | 1860 | break; |
| 1821 | } | 1861 | } |
| 1822 | case OpCode::Id::ST_A: { | 1862 | case OpCode::Id::ST_A: { |
| 1823 | ASSERT_MSG(instr.attribute.fmt20.size == 0, "untested"); | 1863 | ASSERT_MSG(instr.gpr8.Value() == Register::ZeroIndex, |
| 1824 | regs.SetOutputAttributeToRegister(instr.attribute.fmt20.index, | 1864 | "Indirect attribute loads are not supported"); |
| 1825 | instr.attribute.fmt20.element, instr.gpr0); | 1865 | ASSERT_MSG((instr.attribute.fmt20.immediate.Value() % sizeof(u32)) == 0, |
| 1866 | "Unaligned attribute loads are not supported"); | ||
| 1867 | |||
| 1868 | u64 next_element = instr.attribute.fmt20.element; | ||
| 1869 | u64 next_index = static_cast<u64>(instr.attribute.fmt20.index.Value()); | ||
| 1870 | |||
| 1871 | const auto StoreNextElement = [&](u32 reg_offset) { | ||
| 1872 | regs.SetOutputAttributeToRegister(static_cast<Attribute::Index>(next_index), | ||
| 1873 | next_element, | ||
| 1874 | instr.gpr0.Value() + reg_offset); | ||
| 1875 | |||
| 1876 | // Load the next attribute element into the following register. If the element | ||
| 1877 | // to load goes beyond the vec4 size, load the first element of the next | ||
| 1878 | // attribute. | ||
| 1879 | next_element = (next_element + 1) % 4; | ||
| 1880 | next_index = next_index + (next_element == 0 ? 1 : 0); | ||
| 1881 | }; | ||
| 1882 | |||
| 1883 | const u32 num_words = static_cast<u32>(instr.attribute.fmt20.size.Value()) + 1; | ||
| 1884 | for (u32 reg_offset = 0; reg_offset < num_words; ++reg_offset) { | ||
| 1885 | StoreNextElement(reg_offset); | ||
| 1886 | } | ||
| 1887 | |||
| 1826 | break; | 1888 | break; |
| 1827 | } | 1889 | } |
| 1828 | case OpCode::Id::TEX: { | 1890 | case OpCode::Id::TEX: { |
| @@ -1830,6 +1892,13 @@ private: | |||
| 1830 | Tegra::Shader::TextureType texture_type{instr.tex.texture_type}; | 1892 | Tegra::Shader::TextureType texture_type{instr.tex.texture_type}; |
| 1831 | std::string coord; | 1893 | std::string coord; |
| 1832 | 1894 | ||
| 1895 | ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), | ||
| 1896 | "NODEP is not implemented"); | ||
| 1897 | ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), | ||
| 1898 | "AOFFI is not implemented"); | ||
| 1899 | ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC), | ||
| 1900 | "DC is not implemented"); | ||
| 1901 | |||
| 1833 | switch (texture_type) { | 1902 | switch (texture_type) { |
| 1834 | case Tegra::Shader::TextureType::Texture1D: { | 1903 | case Tegra::Shader::TextureType::Texture1D: { |
| 1835 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8); | 1904 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8); |
| @@ -1894,8 +1963,8 @@ private: | |||
| 1894 | UNREACHABLE(); | 1963 | UNREACHABLE(); |
| 1895 | } | 1964 | } |
| 1896 | } | 1965 | } |
| 1897 | size_t dest_elem{}; | 1966 | std::size_t dest_elem{}; |
| 1898 | for (size_t elem = 0; elem < 4; ++elem) { | 1967 | for (std::size_t elem = 0; elem < 4; ++elem) { |
| 1899 | if (!instr.tex.IsComponentEnabled(elem)) { | 1968 | if (!instr.tex.IsComponentEnabled(elem)) { |
| 1900 | // Skip disabled components | 1969 | // Skip disabled components |
| 1901 | continue; | 1970 | continue; |
| @@ -1912,6 +1981,11 @@ private: | |||
| 1912 | Tegra::Shader::TextureType texture_type{instr.texs.GetTextureType()}; | 1981 | Tegra::Shader::TextureType texture_type{instr.texs.GetTextureType()}; |
| 1913 | bool is_array{instr.texs.IsArrayTexture()}; | 1982 | bool is_array{instr.texs.IsArrayTexture()}; |
| 1914 | 1983 | ||
| 1984 | ASSERT_MSG(!instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), | ||
| 1985 | "NODEP is not implemented"); | ||
| 1986 | ASSERT_MSG(!instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC), | ||
| 1987 | "DC is not implemented"); | ||
| 1988 | |||
| 1915 | switch (texture_type) { | 1989 | switch (texture_type) { |
| 1916 | case Tegra::Shader::TextureType::Texture2D: { | 1990 | case Tegra::Shader::TextureType::Texture2D: { |
| 1917 | if (is_array) { | 1991 | if (is_array) { |
| @@ -1948,6 +2022,13 @@ private: | |||
| 1948 | ASSERT(instr.tlds.IsArrayTexture() == false); | 2022 | ASSERT(instr.tlds.IsArrayTexture() == false); |
| 1949 | std::string coord; | 2023 | std::string coord; |
| 1950 | 2024 | ||
| 2025 | ASSERT_MSG(!instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), | ||
| 2026 | "NODEP is not implemented"); | ||
| 2027 | ASSERT_MSG(!instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), | ||
| 2028 | "AOFFI is not implemented"); | ||
| 2029 | ASSERT_MSG(!instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::MZ), | ||
| 2030 | "MZ is not implemented"); | ||
| 2031 | |||
| 1951 | switch (instr.tlds.GetTextureType()) { | 2032 | switch (instr.tlds.GetTextureType()) { |
| 1952 | case Tegra::Shader::TextureType::Texture2D: { | 2033 | case Tegra::Shader::TextureType::Texture2D: { |
| 1953 | if (instr.tlds.IsArrayTexture()) { | 2034 | if (instr.tlds.IsArrayTexture()) { |
| @@ -1976,6 +2057,17 @@ private: | |||
| 1976 | ASSERT(instr.tld4.array == 0); | 2057 | ASSERT(instr.tld4.array == 0); |
| 1977 | std::string coord; | 2058 | std::string coord; |
| 1978 | 2059 | ||
| 2060 | ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), | ||
| 2061 | "NODEP is not implemented"); | ||
| 2062 | ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), | ||
| 2063 | "AOFFI is not implemented"); | ||
| 2064 | ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC), | ||
| 2065 | "DC is not implemented"); | ||
| 2066 | ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV), | ||
| 2067 | "NDV is not implemented"); | ||
| 2068 | ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::PTP), | ||
| 2069 | "PTP is not implemented"); | ||
| 2070 | |||
| 1979 | switch (instr.tld4.texture_type) { | 2071 | switch (instr.tld4.texture_type) { |
| 1980 | case Tegra::Shader::TextureType::Texture2D: { | 2072 | case Tegra::Shader::TextureType::Texture2D: { |
| 1981 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8); | 2073 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8); |
| @@ -1999,8 +2091,8 @@ private: | |||
| 1999 | const std::string texture = "textureGather(" + sampler + ", coords, " + | 2091 | const std::string texture = "textureGather(" + sampler + ", coords, " + |
| 2000 | std::to_string(instr.tld4.component) + ')'; | 2092 | std::to_string(instr.tld4.component) + ')'; |
| 2001 | 2093 | ||
| 2002 | size_t dest_elem{}; | 2094 | std::size_t dest_elem{}; |
| 2003 | for (size_t elem = 0; elem < 4; ++elem) { | 2095 | for (std::size_t elem = 0; elem < 4; ++elem) { |
| 2004 | if (!instr.tex.IsComponentEnabled(elem)) { | 2096 | if (!instr.tex.IsComponentEnabled(elem)) { |
| 2005 | // Skip disabled components | 2097 | // Skip disabled components |
| 2006 | continue; | 2098 | continue; |
| @@ -2013,6 +2105,13 @@ private: | |||
| 2013 | break; | 2105 | break; |
| 2014 | } | 2106 | } |
| 2015 | case OpCode::Id::TLD4S: { | 2107 | case OpCode::Id::TLD4S: { |
| 2108 | ASSERT_MSG(!instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), | ||
| 2109 | "NODEP is not implemented"); | ||
| 2110 | ASSERT_MSG(!instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), | ||
| 2111 | "AOFFI is not implemented"); | ||
| 2112 | ASSERT_MSG(!instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC), | ||
| 2113 | "DC is not implemented"); | ||
| 2114 | |||
| 2016 | const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); | 2115 | const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); |
| 2017 | const std::string op_b = regs.GetRegisterAsFloat(instr.gpr20); | 2116 | const std::string op_b = regs.GetRegisterAsFloat(instr.gpr20); |
| 2018 | // TODO(Subv): Figure out how the sampler type is encoded in the TLD4S instruction. | 2117 | // TODO(Subv): Figure out how the sampler type is encoded in the TLD4S instruction. |
| @@ -2025,6 +2124,9 @@ private: | |||
| 2025 | break; | 2124 | break; |
| 2026 | } | 2125 | } |
| 2027 | case OpCode::Id::TXQ: { | 2126 | case OpCode::Id::TXQ: { |
| 2127 | ASSERT_MSG(!instr.txq.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), | ||
| 2128 | "NODEP is not implemented"); | ||
| 2129 | |||
| 2028 | // TODO: the new commits on the texture refactor, change the way samplers work. | 2130 | // TODO: the new commits on the texture refactor, change the way samplers work. |
| 2029 | // Sadly, not all texture instructions specify the type of texture their sampler | 2131 | // Sadly, not all texture instructions specify the type of texture their sampler |
| 2030 | // uses. This must be fixed at a later instance. | 2132 | // uses. This must be fixed at a later instance. |
| @@ -2045,6 +2147,11 @@ private: | |||
| 2045 | break; | 2147 | break; |
| 2046 | } | 2148 | } |
| 2047 | case OpCode::Id::TMML: { | 2149 | case OpCode::Id::TMML: { |
| 2150 | ASSERT_MSG(!instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), | ||
| 2151 | "NODEP is not implemented"); | ||
| 2152 | ASSERT_MSG(!instr.tmml.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV), | ||
| 2153 | "NDV is not implemented"); | ||
| 2154 | |||
| 2048 | const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); | 2155 | const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); |
| 2049 | const std::string op_b = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); | 2156 | const std::string op_b = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); |
| 2050 | const bool is_array = instr.tmml.array != 0; | 2157 | const bool is_array = instr.tmml.array != 0; |
| @@ -2211,31 +2318,55 @@ private: | |||
| 2211 | break; | 2318 | break; |
| 2212 | } | 2319 | } |
| 2213 | case OpCode::Type::PredicateSetPredicate: { | 2320 | case OpCode::Type::PredicateSetPredicate: { |
| 2214 | const std::string op_a = | 2321 | switch (opcode->GetId()) { |
| 2215 | GetPredicateCondition(instr.psetp.pred12, instr.psetp.neg_pred12 != 0); | 2322 | case OpCode::Id::PSETP: { |
| 2216 | const std::string op_b = | 2323 | const std::string op_a = |
| 2217 | GetPredicateCondition(instr.psetp.pred29, instr.psetp.neg_pred29 != 0); | 2324 | GetPredicateCondition(instr.psetp.pred12, instr.psetp.neg_pred12 != 0); |
| 2325 | const std::string op_b = | ||
| 2326 | GetPredicateCondition(instr.psetp.pred29, instr.psetp.neg_pred29 != 0); | ||
| 2218 | 2327 | ||
| 2219 | // We can't use the constant predicate as destination. | 2328 | // We can't use the constant predicate as destination. |
| 2220 | ASSERT(instr.psetp.pred3 != static_cast<u64>(Pred::UnusedIndex)); | 2329 | ASSERT(instr.psetp.pred3 != static_cast<u64>(Pred::UnusedIndex)); |
| 2221 | 2330 | ||
| 2222 | const std::string second_pred = | 2331 | const std::string second_pred = |
| 2223 | GetPredicateCondition(instr.psetp.pred39, instr.psetp.neg_pred39 != 0); | 2332 | GetPredicateCondition(instr.psetp.pred39, instr.psetp.neg_pred39 != 0); |
| 2224 | 2333 | ||
| 2225 | const std::string combiner = GetPredicateCombiner(instr.psetp.op); | 2334 | const std::string combiner = GetPredicateCombiner(instr.psetp.op); |
| 2226 | 2335 | ||
| 2227 | const std::string predicate = | 2336 | const std::string predicate = |
| 2228 | '(' + op_a + ") " + GetPredicateCombiner(instr.psetp.cond) + " (" + op_b + ')'; | 2337 | '(' + op_a + ") " + GetPredicateCombiner(instr.psetp.cond) + " (" + op_b + ')'; |
| 2229 | 2338 | ||
| 2230 | // Set the primary predicate to the result of Predicate OP SecondPredicate | 2339 | // Set the primary predicate to the result of Predicate OP SecondPredicate |
| 2231 | SetPredicate(instr.psetp.pred3, | 2340 | SetPredicate(instr.psetp.pred3, |
| 2232 | '(' + predicate + ") " + combiner + " (" + second_pred + ')'); | 2341 | '(' + predicate + ") " + combiner + " (" + second_pred + ')'); |
| 2233 | 2342 | ||
| 2234 | if (instr.psetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) { | 2343 | if (instr.psetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) { |
| 2235 | // Set the secondary predicate to the result of !Predicate OP SecondPredicate, | 2344 | // Set the secondary predicate to the result of !Predicate OP SecondPredicate, |
| 2236 | // if enabled | 2345 | // if enabled |
| 2237 | SetPredicate(instr.psetp.pred0, | 2346 | SetPredicate(instr.psetp.pred0, |
| 2238 | "!(" + predicate + ") " + combiner + " (" + second_pred + ')'); | 2347 | "!(" + predicate + ") " + combiner + " (" + second_pred + ')'); |
| 2348 | } | ||
| 2349 | break; | ||
| 2350 | } | ||
| 2351 | case OpCode::Id::CSETP: { | ||
| 2352 | const std::string pred = | ||
| 2353 | GetPredicateCondition(instr.csetp.pred39, instr.csetp.neg_pred39 != 0); | ||
| 2354 | const std::string combiner = GetPredicateCombiner(instr.csetp.op); | ||
| 2355 | const std::string controlCode = regs.GetControlCode(instr.csetp.cc); | ||
| 2356 | if (instr.csetp.pred3 != static_cast<u64>(Pred::UnusedIndex)) { | ||
| 2357 | SetPredicate(instr.csetp.pred3, | ||
| 2358 | '(' + controlCode + ") " + combiner + " (" + pred + ')'); | ||
| 2359 | } | ||
| 2360 | if (instr.csetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) { | ||
| 2361 | SetPredicate(instr.csetp.pred0, | ||
| 2362 | "!(" + controlCode + ") " + combiner + " (" + pred + ')'); | ||
| 2363 | } | ||
| 2364 | break; | ||
| 2365 | } | ||
| 2366 | default: { | ||
| 2367 | LOG_CRITICAL(HW_GPU, "Unhandled predicate instruction: {}", opcode->GetName()); | ||
| 2368 | UNREACHABLE(); | ||
| 2369 | } | ||
| 2239 | } | 2370 | } |
| 2240 | break; | 2371 | break; |
| 2241 | } | 2372 | } |
| @@ -2625,6 +2756,7 @@ private: | |||
| 2625 | private: | 2756 | private: |
| 2626 | const std::set<Subroutine>& subroutines; | 2757 | const std::set<Subroutine>& subroutines; |
| 2627 | const ProgramCode& program_code; | 2758 | const ProgramCode& program_code; |
| 2759 | Tegra::Shader::Header header; | ||
| 2628 | const u32 main_offset; | 2760 | const u32 main_offset; |
| 2629 | Maxwell3D::Regs::ShaderStage stage; | 2761 | Maxwell3D::Regs::ShaderStage stage; |
| 2630 | const std::string& suffix; | 2762 | const std::string& suffix; |
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h index a43e2997b..d53b93ad5 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.h +++ b/src/video_core/renderer_opengl/gl_shader_gen.h | |||
| @@ -13,7 +13,7 @@ | |||
| 13 | 13 | ||
| 14 | namespace OpenGL::GLShader { | 14 | namespace OpenGL::GLShader { |
| 15 | 15 | ||
| 16 | constexpr size_t MAX_PROGRAM_CODE_LENGTH{0x1000}; | 16 | constexpr std::size_t MAX_PROGRAM_CODE_LENGTH{0x1000}; |
| 17 | using ProgramCode = std::vector<u64>; | 17 | using ProgramCode = std::vector<u64>; |
| 18 | 18 | ||
| 19 | class ConstBufferEntry { | 19 | class ConstBufferEntry { |
| @@ -51,7 +51,7 @@ public: | |||
| 51 | } | 51 | } |
| 52 | 52 | ||
| 53 | std::string GetName() const { | 53 | std::string GetName() const { |
| 54 | return BufferBaseNames[static_cast<size_t>(stage)] + std::to_string(index); | 54 | return BufferBaseNames[static_cast<std::size_t>(stage)] + std::to_string(index); |
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | u32 GetHash() const { | 57 | u32 GetHash() const { |
| @@ -74,15 +74,15 @@ class SamplerEntry { | |||
| 74 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; | 74 | using Maxwell = Tegra::Engines::Maxwell3D::Regs; |
| 75 | 75 | ||
| 76 | public: | 76 | public: |
| 77 | SamplerEntry(Maxwell::ShaderStage stage, size_t offset, size_t index, | 77 | SamplerEntry(Maxwell::ShaderStage stage, std::size_t offset, std::size_t index, |
| 78 | Tegra::Shader::TextureType type, bool is_array) | 78 | Tegra::Shader::TextureType type, bool is_array) |
| 79 | : offset(offset), stage(stage), sampler_index(index), type(type), is_array(is_array) {} | 79 | : offset(offset), stage(stage), sampler_index(index), type(type), is_array(is_array) {} |
| 80 | 80 | ||
| 81 | size_t GetOffset() const { | 81 | std::size_t GetOffset() const { |
| 82 | return offset; | 82 | return offset; |
| 83 | } | 83 | } |
| 84 | 84 | ||
| 85 | size_t GetIndex() const { | 85 | std::size_t GetIndex() const { |
| 86 | return sampler_index; | 86 | return sampler_index; |
| 87 | } | 87 | } |
| 88 | 88 | ||
| @@ -91,7 +91,7 @@ public: | |||
| 91 | } | 91 | } |
| 92 | 92 | ||
| 93 | std::string GetName() const { | 93 | std::string GetName() const { |
| 94 | return std::string(TextureSamplerNames[static_cast<size_t>(stage)]) + '_' + | 94 | return std::string(TextureSamplerNames[static_cast<std::size_t>(stage)]) + '_' + |
| 95 | std::to_string(sampler_index); | 95 | std::to_string(sampler_index); |
| 96 | } | 96 | } |
| 97 | 97 | ||
| @@ -133,7 +133,7 @@ public: | |||
| 133 | } | 133 | } |
| 134 | 134 | ||
| 135 | static std::string GetArrayName(Maxwell::ShaderStage stage) { | 135 | static std::string GetArrayName(Maxwell::ShaderStage stage) { |
| 136 | return TextureSamplerNames[static_cast<size_t>(stage)]; | 136 | return TextureSamplerNames[static_cast<std::size_t>(stage)]; |
| 137 | } | 137 | } |
| 138 | 138 | ||
| 139 | private: | 139 | private: |
| @@ -143,9 +143,9 @@ private: | |||
| 143 | 143 | ||
| 144 | /// Offset in TSC memory from which to read the sampler object, as specified by the sampling | 144 | /// Offset in TSC memory from which to read the sampler object, as specified by the sampling |
| 145 | /// instruction. | 145 | /// instruction. |
| 146 | size_t offset; | 146 | std::size_t offset; |
| 147 | Maxwell::ShaderStage stage; ///< Shader stage where this sampler was used. | 147 | Maxwell::ShaderStage stage; ///< Shader stage where this sampler was used. |
| 148 | size_t sampler_index; ///< Value used to index into the generated GLSL sampler array. | 148 | std::size_t sampler_index; ///< Value used to index into the generated GLSL sampler array. |
| 149 | Tegra::Shader::TextureType type; ///< The type used to sample this texture (Texture2D, etc) | 149 | Tegra::Shader::TextureType type; ///< The type used to sample this texture (Texture2D, etc) |
| 150 | bool is_array; ///< Whether the texture is being sampled as an array texture or not. | 150 | bool is_array; ///< Whether the texture is being sampled as an array texture or not. |
| 151 | }; | 151 | }; |
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h index 533e42caa..3de15ba9b 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.h +++ b/src/video_core/renderer_opengl/gl_shader_manager.h | |||
| @@ -11,9 +11,6 @@ | |||
| 11 | 11 | ||
| 12 | namespace OpenGL::GLShader { | 12 | namespace OpenGL::GLShader { |
| 13 | 13 | ||
| 14 | /// Number of OpenGL texture samplers that can be used in the fragment shader | ||
| 15 | static constexpr size_t NumTextureSamplers = 32; | ||
| 16 | |||
| 17 | using Tegra::Engines::Maxwell3D; | 14 | using Tegra::Engines::Maxwell3D; |
| 18 | 15 | ||
| 19 | /// Uniform structure for the Uniform Buffer Object, all vectors must be 16-byte aligned | 16 | /// Uniform structure for the Uniform Buffer Object, all vectors must be 16-byte aligned |
diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index 6f70deb96..1fe26a2a9 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include <iterator> | 5 | #include <iterator> |
| 6 | #include <glad/glad.h> | 6 | #include <glad/glad.h> |
| 7 | #include "common/assert.h" | ||
| 7 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 8 | #include "video_core/renderer_opengl/gl_state.h" | 9 | #include "video_core/renderer_opengl/gl_state.h" |
| 9 | 10 | ||
| @@ -78,6 +79,8 @@ OpenGLState::OpenGLState() { | |||
| 78 | viewport.height = 0; | 79 | viewport.height = 0; |
| 79 | 80 | ||
| 80 | clip_distance = {}; | 81 | clip_distance = {}; |
| 82 | |||
| 83 | point.size = 1; | ||
| 81 | } | 84 | } |
| 82 | 85 | ||
| 83 | void OpenGLState::Apply() const { | 86 | void OpenGLState::Apply() const { |
| @@ -204,9 +207,6 @@ void OpenGLState::Apply() const { | |||
| 204 | glActiveTexture(TextureUnits::MaxwellTexture(static_cast<int>(i)).Enum()); | 207 | glActiveTexture(TextureUnits::MaxwellTexture(static_cast<int>(i)).Enum()); |
| 205 | glBindTexture(texture_unit.target, texture_unit.texture); | 208 | glBindTexture(texture_unit.target, texture_unit.texture); |
| 206 | } | 209 | } |
| 207 | if (texture_unit.sampler != cur_state_texture_unit.sampler) { | ||
| 208 | glBindSampler(static_cast<GLuint>(i), texture_unit.sampler); | ||
| 209 | } | ||
| 210 | // Update the texture swizzle | 210 | // Update the texture swizzle |
| 211 | if (texture_unit.swizzle.r != cur_state_texture_unit.swizzle.r || | 211 | if (texture_unit.swizzle.r != cur_state_texture_unit.swizzle.r || |
| 212 | texture_unit.swizzle.g != cur_state_texture_unit.swizzle.g || | 212 | texture_unit.swizzle.g != cur_state_texture_unit.swizzle.g || |
| @@ -218,6 +218,27 @@ void OpenGLState::Apply() const { | |||
| 218 | } | 218 | } |
| 219 | } | 219 | } |
| 220 | 220 | ||
| 221 | // Samplers | ||
| 222 | { | ||
| 223 | bool has_delta{}; | ||
| 224 | std::size_t first{}, last{}; | ||
| 225 | std::array<GLuint, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> samplers; | ||
| 226 | for (std::size_t i = 0; i < std::size(samplers); ++i) { | ||
| 227 | samplers[i] = texture_units[i].sampler; | ||
| 228 | if (samplers[i] != cur_state.texture_units[i].sampler) { | ||
| 229 | if (!has_delta) { | ||
| 230 | first = i; | ||
| 231 | has_delta = true; | ||
| 232 | } | ||
| 233 | last = i; | ||
| 234 | } | ||
| 235 | } | ||
| 236 | if (has_delta) { | ||
| 237 | glBindSamplers(static_cast<GLuint>(first), static_cast<GLsizei>(last - first + 1), | ||
| 238 | samplers.data()); | ||
| 239 | } | ||
| 240 | } | ||
| 241 | |||
| 221 | // Framebuffer | 242 | // Framebuffer |
| 222 | if (draw.read_framebuffer != cur_state.draw.read_framebuffer) { | 243 | if (draw.read_framebuffer != cur_state.draw.read_framebuffer) { |
| 223 | glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer); | 244 | glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer); |
| @@ -272,7 +293,7 @@ void OpenGLState::Apply() const { | |||
| 272 | } | 293 | } |
| 273 | 294 | ||
| 274 | // Clip distance | 295 | // Clip distance |
| 275 | for (size_t i = 0; i < clip_distance.size(); ++i) { | 296 | for (std::size_t i = 0; i < clip_distance.size(); ++i) { |
| 276 | if (clip_distance[i] != cur_state.clip_distance[i]) { | 297 | if (clip_distance[i] != cur_state.clip_distance[i]) { |
| 277 | if (clip_distance[i]) { | 298 | if (clip_distance[i]) { |
| 278 | glEnable(GL_CLIP_DISTANCE0 + static_cast<GLenum>(i)); | 299 | glEnable(GL_CLIP_DISTANCE0 + static_cast<GLenum>(i)); |
| @@ -282,6 +303,11 @@ void OpenGLState::Apply() const { | |||
| 282 | } | 303 | } |
| 283 | } | 304 | } |
| 284 | 305 | ||
| 306 | // Point | ||
| 307 | if (point.size != cur_state.point.size) { | ||
| 308 | glPointSize(point.size); | ||
| 309 | } | ||
| 310 | |||
| 285 | cur_state = *this; | 311 | cur_state = *this; |
| 286 | } | 312 | } |
| 287 | 313 | ||
diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index e3e24b9e7..dc21a2ee3 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h | |||
| @@ -6,13 +6,10 @@ | |||
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <glad/glad.h> | 8 | #include <glad/glad.h> |
| 9 | |||
| 10 | #include "video_core/engines/maxwell_3d.h" | 9 | #include "video_core/engines/maxwell_3d.h" |
| 11 | 10 | ||
| 12 | namespace OpenGL { | 11 | namespace OpenGL { |
| 13 | 12 | ||
| 14 | using Regs = Tegra::Engines::Maxwell3D::Regs; | ||
| 15 | |||
| 16 | namespace TextureUnits { | 13 | namespace TextureUnits { |
| 17 | 14 | ||
| 18 | struct TextureUnit { | 15 | struct TextureUnit { |
| @@ -118,7 +115,7 @@ public: | |||
| 118 | target = GL_TEXTURE_2D; | 115 | target = GL_TEXTURE_2D; |
| 119 | } | 116 | } |
| 120 | }; | 117 | }; |
| 121 | std::array<TextureUnit, 32> texture_units; | 118 | std::array<TextureUnit, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> texture_units; |
| 122 | 119 | ||
| 123 | struct { | 120 | struct { |
| 124 | GLuint read_framebuffer; // GL_READ_FRAMEBUFFER_BINDING | 121 | GLuint read_framebuffer; // GL_READ_FRAMEBUFFER_BINDING |
| @@ -145,6 +142,10 @@ public: | |||
| 145 | GLsizei height; | 142 | GLsizei height; |
| 146 | } viewport; | 143 | } viewport; |
| 147 | 144 | ||
| 145 | struct { | ||
| 146 | float size; // GL_POINT_SIZE | ||
| 147 | } point; | ||
| 148 | |||
| 148 | std::array<bool, 2> clip_distance; // GL_CLIP_DISTANCE | 149 | std::array<bool, 2> clip_distance; // GL_CLIP_DISTANCE |
| 149 | 150 | ||
| 150 | OpenGLState(); | 151 | OpenGLState(); |
diff --git a/src/video_core/renderer_opengl/gl_stream_buffer.cpp b/src/video_core/renderer_opengl/gl_stream_buffer.cpp index aadf68f16..e409228cc 100644 --- a/src/video_core/renderer_opengl/gl_stream_buffer.cpp +++ b/src/video_core/renderer_opengl/gl_stream_buffer.cpp | |||
| @@ -61,7 +61,7 @@ std::tuple<u8*, GLintptr, bool> OGLStreamBuffer::Map(GLsizeiptr size, GLintptr a | |||
| 61 | mapped_size = size; | 61 | mapped_size = size; |
| 62 | 62 | ||
| 63 | if (alignment > 0) { | 63 | if (alignment > 0) { |
| 64 | buffer_pos = Common::AlignUp<size_t>(buffer_pos, alignment); | 64 | buffer_pos = Common::AlignUp<std::size_t>(buffer_pos, alignment); |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | bool invalidate = false; | 67 | bool invalidate = false; |
| @@ -74,7 +74,7 @@ std::tuple<u8*, GLintptr, bool> OGLStreamBuffer::Map(GLsizeiptr size, GLintptr a | |||
| 74 | } | 74 | } |
| 75 | } | 75 | } |
| 76 | 76 | ||
| 77 | if (invalidate | !persistent) { | 77 | if (invalidate || !persistent) { |
| 78 | GLbitfield flags = GL_MAP_WRITE_BIT | (persistent ? GL_MAP_PERSISTENT_BIT : 0) | | 78 | GLbitfield flags = GL_MAP_WRITE_BIT | (persistent ? GL_MAP_PERSISTENT_BIT : 0) | |
| 79 | (coherent ? GL_MAP_COHERENT_BIT : GL_MAP_FLUSH_EXPLICIT_BIT) | | 79 | (coherent ? GL_MAP_COHERENT_BIT : GL_MAP_FLUSH_EXPLICIT_BIT) | |
| 80 | (invalidate ? GL_MAP_INVALIDATE_BUFFER_BIT : GL_MAP_UNSYNCHRONIZED_BIT); | 80 | (invalidate ? GL_MAP_INVALIDATE_BUFFER_BIT : GL_MAP_UNSYNCHRONIZED_BIT); |
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index 272294c62..3d5476e5d 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp | |||
| @@ -13,39 +13,101 @@ | |||
| 13 | namespace Tegra::Texture { | 13 | namespace Tegra::Texture { |
| 14 | 14 | ||
| 15 | /** | 15 | /** |
| 16 | * This table represents the internal swizzle of a gob, | ||
| 17 | * in format 16 bytes x 2 sector packing. | ||
| 16 | * Calculates the offset of an (x, y) position within a swizzled texture. | 18 | * Calculates the offset of an (x, y) position within a swizzled texture. |
| 17 | * Taken from the Tegra X1 TRM. | 19 | * Taken from the Tegra X1 Technical Reference Manual. pages 1187-1188 |
| 18 | */ | 20 | */ |
| 19 | static u32 GetSwizzleOffset(u32 x, u32 y, u32 image_width, u32 bytes_per_pixel, u32 block_height) { | 21 | template <std::size_t N, std::size_t M, u32 Align> |
| 20 | // Round up to the next gob | 22 | struct alignas(64) SwizzleTable { |
| 21 | const u32 image_width_in_gobs{(image_width * bytes_per_pixel + 63) / 64}; | 23 | static_assert(M * Align == 64, "Swizzle Table does not align to GOB"); |
| 24 | constexpr SwizzleTable() { | ||
| 25 | for (u32 y = 0; y < N; ++y) { | ||
| 26 | for (u32 x = 0; x < M; ++x) { | ||
| 27 | const u32 x2 = x * Align; | ||
| 28 | values[y][x] = static_cast<u16>(((x2 % 64) / 32) * 256 + ((y % 8) / 2) * 64 + | ||
| 29 | ((x2 % 32) / 16) * 32 + (y % 2) * 16 + (x2 % 16)); | ||
| 30 | } | ||
| 31 | } | ||
| 32 | } | ||
| 33 | const std::array<u16, M>& operator[](std::size_t index) const { | ||
| 34 | return values[index]; | ||
| 35 | } | ||
| 36 | std::array<std::array<u16, M>, N> values{}; | ||
| 37 | }; | ||
| 22 | 38 | ||
| 23 | u32 GOB_address = 0 + (y / (8 * block_height)) * 512 * block_height * image_width_in_gobs + | 39 | constexpr auto legacy_swizzle_table = SwizzleTable<8, 64, 1>(); |
| 24 | (x * bytes_per_pixel / 64) * 512 * block_height + | 40 | constexpr auto fast_swizzle_table = SwizzleTable<8, 4, 16>(); |
| 25 | (y % (8 * block_height) / 8) * 512; | ||
| 26 | x *= bytes_per_pixel; | ||
| 27 | u32 address = GOB_address + ((x % 64) / 32) * 256 + ((y % 8) / 2) * 64 + ((x % 32) / 16) * 32 + | ||
| 28 | (y % 2) * 16 + (x % 16); | ||
| 29 | 41 | ||
| 30 | return address; | 42 | static void LegacySwizzleData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel, |
| 31 | } | 43 | u8* swizzled_data, u8* unswizzled_data, bool unswizzle, |
| 32 | 44 | u32 block_height) { | |
| 33 | void CopySwizzledData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel, | 45 | std::array<u8*, 2> data_ptrs; |
| 34 | u8* swizzled_data, u8* unswizzled_data, bool unswizzle, u32 block_height) { | 46 | const std::size_t stride = width * bytes_per_pixel; |
| 35 | u8* data_ptrs[2]; | 47 | const std::size_t gobs_in_x = 64; |
| 36 | for (unsigned y = 0; y < height; ++y) { | 48 | const std::size_t gobs_in_y = 8; |
| 37 | for (unsigned x = 0; x < width; ++x) { | 49 | const std::size_t gobs_size = gobs_in_x * gobs_in_y; |
| 38 | u32 swizzle_offset = GetSwizzleOffset(x, y, width, bytes_per_pixel, block_height); | 50 | const std::size_t image_width_in_gobs{(stride + gobs_in_x - 1) / gobs_in_x}; |
| 39 | u32 pixel_index = (x + y * width) * out_bytes_per_pixel; | 51 | for (std::size_t y = 0; y < height; ++y) { |
| 52 | const std::size_t gob_y_address = | ||
| 53 | (y / (gobs_in_y * block_height)) * gobs_size * block_height * image_width_in_gobs + | ||
| 54 | (y % (gobs_in_y * block_height) / gobs_in_y) * gobs_size; | ||
| 55 | const auto& table = legacy_swizzle_table[y % gobs_in_y]; | ||
| 56 | for (std::size_t x = 0; x < width; ++x) { | ||
| 57 | const std::size_t gob_address = | ||
| 58 | gob_y_address + (x * bytes_per_pixel / gobs_in_x) * gobs_size * block_height; | ||
| 59 | const std::size_t x2 = x * bytes_per_pixel; | ||
| 60 | const std::size_t swizzle_offset = gob_address + table[x2 % gobs_in_x]; | ||
| 61 | const std::size_t pixel_index = (x + y * width) * out_bytes_per_pixel; | ||
| 40 | 62 | ||
| 41 | data_ptrs[unswizzle] = swizzled_data + swizzle_offset; | 63 | data_ptrs[unswizzle] = swizzled_data + swizzle_offset; |
| 42 | data_ptrs[!unswizzle] = &unswizzled_data[pixel_index]; | 64 | data_ptrs[!unswizzle] = unswizzled_data + pixel_index; |
| 43 | 65 | ||
| 44 | std::memcpy(data_ptrs[0], data_ptrs[1], bytes_per_pixel); | 66 | std::memcpy(data_ptrs[0], data_ptrs[1], bytes_per_pixel); |
| 45 | } | 67 | } |
| 46 | } | 68 | } |
| 47 | } | 69 | } |
| 48 | 70 | ||
| 71 | static void FastSwizzleData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel, | ||
| 72 | u8* swizzled_data, u8* unswizzled_data, bool unswizzle, | ||
| 73 | u32 block_height) { | ||
| 74 | std::array<u8*, 2> data_ptrs; | ||
| 75 | const std::size_t stride{width * bytes_per_pixel}; | ||
| 76 | const std::size_t gobs_in_x = 64; | ||
| 77 | const std::size_t gobs_in_y = 8; | ||
| 78 | const std::size_t gobs_size = gobs_in_x * gobs_in_y; | ||
| 79 | const std::size_t image_width_in_gobs{(stride + gobs_in_x - 1) / gobs_in_x}; | ||
| 80 | const std::size_t copy_size{16}; | ||
| 81 | for (std::size_t y = 0; y < height; ++y) { | ||
| 82 | const std::size_t initial_gob = | ||
| 83 | (y / (gobs_in_y * block_height)) * gobs_size * block_height * image_width_in_gobs + | ||
| 84 | (y % (gobs_in_y * block_height) / gobs_in_y) * gobs_size; | ||
| 85 | const std::size_t pixel_base{y * width * out_bytes_per_pixel}; | ||
| 86 | const auto& table = fast_swizzle_table[y % gobs_in_y]; | ||
| 87 | for (std::size_t xb = 0; xb < stride; xb += copy_size) { | ||
| 88 | const std::size_t gob_address{initial_gob + | ||
| 89 | (xb / gobs_in_x) * gobs_size * block_height}; | ||
| 90 | const std::size_t swizzle_offset{gob_address + table[(xb / 16) % 4]}; | ||
| 91 | const std::size_t out_x = xb * out_bytes_per_pixel / bytes_per_pixel; | ||
| 92 | const std::size_t pixel_index{out_x + pixel_base}; | ||
| 93 | data_ptrs[unswizzle] = swizzled_data + swizzle_offset; | ||
| 94 | data_ptrs[!unswizzle] = unswizzled_data + pixel_index; | ||
| 95 | std::memcpy(data_ptrs[0], data_ptrs[1], copy_size); | ||
| 96 | } | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 100 | void CopySwizzledData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel, | ||
| 101 | u8* swizzled_data, u8* unswizzled_data, bool unswizzle, u32 block_height) { | ||
| 102 | if (bytes_per_pixel % 3 != 0 && (width * bytes_per_pixel) % 16 == 0) { | ||
| 103 | FastSwizzleData(width, height, bytes_per_pixel, out_bytes_per_pixel, swizzled_data, | ||
| 104 | unswizzled_data, unswizzle, block_height); | ||
| 105 | } else { | ||
| 106 | LegacySwizzleData(width, height, bytes_per_pixel, out_bytes_per_pixel, swizzled_data, | ||
| 107 | unswizzled_data, unswizzle, block_height); | ||
| 108 | } | ||
| 109 | } | ||
| 110 | |||
| 49 | u32 BytesPerPixel(TextureFormat format) { | 111 | u32 BytesPerPixel(TextureFormat format) { |
| 50 | switch (format) { | 112 | switch (format) { |
| 51 | case TextureFormat::DXT1: | 113 | case TextureFormat::DXT1: |
| @@ -63,6 +125,7 @@ u32 BytesPerPixel(TextureFormat format) { | |||
| 63 | case TextureFormat::R32_G32_B32: | 125 | case TextureFormat::R32_G32_B32: |
| 64 | return 12; | 126 | return 12; |
| 65 | case TextureFormat::ASTC_2D_4X4: | 127 | case TextureFormat::ASTC_2D_4X4: |
| 128 | case TextureFormat::ASTC_2D_8X8: | ||
| 66 | case TextureFormat::A8R8G8B8: | 129 | case TextureFormat::A8R8G8B8: |
| 67 | case TextureFormat::A2B10G10R10: | 130 | case TextureFormat::A2B10G10R10: |
| 68 | case TextureFormat::BF10GF11RF11: | 131 | case TextureFormat::BF10GF11RF11: |
| @@ -111,6 +174,7 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat | |||
| 111 | case TextureFormat::BC6H_UF16: | 174 | case TextureFormat::BC6H_UF16: |
| 112 | case TextureFormat::BC6H_SF16: | 175 | case TextureFormat::BC6H_SF16: |
| 113 | case TextureFormat::ASTC_2D_4X4: | 176 | case TextureFormat::ASTC_2D_4X4: |
| 177 | case TextureFormat::ASTC_2D_8X8: | ||
| 114 | case TextureFormat::A8R8G8B8: | 178 | case TextureFormat::A8R8G8B8: |
| 115 | case TextureFormat::A2B10G10R10: | 179 | case TextureFormat::A2B10G10R10: |
| 116 | case TextureFormat::A1B5G5R5: | 180 | case TextureFormat::A1B5G5R5: |
diff --git a/src/video_core/utils.h b/src/video_core/utils.h index e0a14d48f..681919ae3 100644 --- a/src/video_core/utils.h +++ b/src/video_core/utils.h | |||
| @@ -161,4 +161,26 @@ static inline void MortonCopyPixels128(u32 width, u32 height, u32 bytes_per_pixe | |||
| 161 | } | 161 | } |
| 162 | } | 162 | } |
| 163 | 163 | ||
| 164 | static void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr, | ||
| 165 | std::string extra_info = "") { | ||
| 166 | if (!GLAD_GL_KHR_debug) { | ||
| 167 | return; // We don't need to throw an error as this is just for debugging | ||
| 168 | } | ||
| 169 | const std::string nice_addr = fmt::format("0x{:016x}", addr); | ||
| 170 | std::string object_label; | ||
| 171 | |||
| 172 | switch (identifier) { | ||
| 173 | case GL_TEXTURE: | ||
| 174 | object_label = extra_info + "@" + nice_addr; | ||
| 175 | break; | ||
| 176 | case GL_PROGRAM: | ||
| 177 | object_label = "ShaderProgram@" + nice_addr; | ||
| 178 | break; | ||
| 179 | default: | ||
| 180 | object_label = fmt::format("Object(0x{:x})@{}", identifier, nice_addr); | ||
| 181 | break; | ||
| 182 | } | ||
| 183 | glObjectLabel(identifier, handle, -1, static_cast<const GLchar*>(object_label.c_str())); | ||
| 184 | } | ||
| 185 | |||
| 164 | } // namespace VideoCore | 186 | } // namespace VideoCore |
diff --git a/src/yuzu/configuration/configure_gamelist.cpp b/src/yuzu/configuration/configure_gamelist.cpp index 0be030434..8743ce982 100644 --- a/src/yuzu/configuration/configure_gamelist.cpp +++ b/src/yuzu/configuration/configure_gamelist.cpp | |||
| @@ -89,7 +89,7 @@ void ConfigureGameList::InitializeIconSizeComboBox() { | |||
| 89 | } | 89 | } |
| 90 | 90 | ||
| 91 | void ConfigureGameList::InitializeRowComboBoxes() { | 91 | void ConfigureGameList::InitializeRowComboBoxes() { |
| 92 | for (size_t i = 0; i < row_text_names.size(); ++i) { | 92 | for (std::size_t i = 0; i < row_text_names.size(); ++i) { |
| 93 | ui->row_1_text_combobox->addItem(row_text_names[i], QVariant::fromValue(i)); | 93 | ui->row_1_text_combobox->addItem(row_text_names[i], QVariant::fromValue(i)); |
| 94 | ui->row_2_text_combobox->addItem(row_text_names[i], QVariant::fromValue(i)); | 94 | ui->row_2_text_combobox->addItem(row_text_names[i], QVariant::fromValue(i)); |
| 95 | } | 95 | } |
diff --git a/src/yuzu/debugger/graphics/graphics_breakpoints.cpp b/src/yuzu/debugger/graphics/graphics_breakpoints.cpp index fe682b3b8..b5c88f944 100644 --- a/src/yuzu/debugger/graphics/graphics_breakpoints.cpp +++ b/src/yuzu/debugger/graphics/graphics_breakpoints.cpp | |||
| @@ -42,7 +42,8 @@ QVariant BreakPointModel::data(const QModelIndex& index, int role) const { | |||
| 42 | tr("Finished primitive batch")}, | 42 | tr("Finished primitive batch")}, |
| 43 | }; | 43 | }; |
| 44 | 44 | ||
| 45 | DEBUG_ASSERT(map.size() == static_cast<size_t>(Tegra::DebugContext::Event::NumEvents)); | 45 | DEBUG_ASSERT(map.size() == |
| 46 | static_cast<std::size_t>(Tegra::DebugContext::Event::NumEvents)); | ||
| 46 | return (map.find(event) != map.end()) ? map.at(event) : QString(); | 47 | return (map.find(event) != map.end()) ? map.at(event) : QString(); |
| 47 | } | 48 | } |
| 48 | 49 | ||
diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp index 7e37962d5..cbcd5dd5f 100644 --- a/src/yuzu/debugger/graphics/graphics_surface.cpp +++ b/src/yuzu/debugger/graphics/graphics_surface.cpp | |||
| @@ -341,8 +341,8 @@ void GraphicsSurfaceWidget::OnUpdate() { | |||
| 341 | // directly... | 341 | // directly... |
| 342 | 342 | ||
| 343 | const auto& registers = gpu.Maxwell3D().regs; | 343 | const auto& registers = gpu.Maxwell3D().regs; |
| 344 | const auto& rt = registers.rt[static_cast<size_t>(surface_source) - | 344 | const auto& rt = registers.rt[static_cast<std::size_t>(surface_source) - |
| 345 | static_cast<size_t>(Source::RenderTarget0)]; | 345 | static_cast<std::size_t>(Source::RenderTarget0)]; |
| 346 | 346 | ||
| 347 | surface_address = rt.Address(); | 347 | surface_address = rt.Address(); |
| 348 | surface_width = rt.width; | 348 | surface_width = rt.width; |
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index dc1023113..a3b1fd357 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | #include "core/hle/kernel/thread.h" | 15 | #include "core/hle/kernel/thread.h" |
| 16 | #include "core/hle/kernel/timer.h" | 16 | #include "core/hle/kernel/timer.h" |
| 17 | #include "core/hle/kernel/wait_object.h" | 17 | #include "core/hle/kernel/wait_object.h" |
| 18 | #include "core/memory.h" | ||
| 18 | 19 | ||
| 19 | WaitTreeItem::WaitTreeItem() = default; | 20 | WaitTreeItem::WaitTreeItem() = default; |
| 20 | WaitTreeItem::~WaitTreeItem() = default; | 21 | WaitTreeItem::~WaitTreeItem() = default; |
| @@ -117,7 +118,7 @@ QString WaitTreeCallstack::GetText() const { | |||
| 117 | std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeCallstack::GetChildren() const { | 118 | std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeCallstack::GetChildren() const { |
| 118 | std::vector<std::unique_ptr<WaitTreeItem>> list; | 119 | std::vector<std::unique_ptr<WaitTreeItem>> list; |
| 119 | 120 | ||
| 120 | constexpr size_t BaseRegister = 29; | 121 | constexpr std::size_t BaseRegister = 29; |
| 121 | u64 base_pointer = thread.context.cpu_registers[BaseRegister]; | 122 | u64 base_pointer = thread.context.cpu_registers[BaseRegister]; |
| 122 | 123 | ||
| 123 | while (base_pointer != 0) { | 124 | while (base_pointer != 0) { |
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 3b3b551bb..67890455a 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp | |||
| @@ -26,10 +26,10 @@ | |||
| 26 | #include "yuzu/main.h" | 26 | #include "yuzu/main.h" |
| 27 | #include "yuzu/ui_settings.h" | 27 | #include "yuzu/ui_settings.h" |
| 28 | 28 | ||
| 29 | GameList::SearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist) : gamelist{gamelist} {} | 29 | GameListSearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist) : gamelist{gamelist} {} |
| 30 | 30 | ||
| 31 | // EventFilter in order to process systemkeys while editing the searchfield | 31 | // EventFilter in order to process systemkeys while editing the searchfield |
| 32 | bool GameList::SearchField::KeyReleaseEater::eventFilter(QObject* obj, QEvent* event) { | 32 | bool GameListSearchField::KeyReleaseEater::eventFilter(QObject* obj, QEvent* event) { |
| 33 | // If it isn't a KeyRelease event then continue with standard event processing | 33 | // If it isn't a KeyRelease event then continue with standard event processing |
| 34 | if (event->type() != QEvent::KeyRelease) | 34 | if (event->type() != QEvent::KeyRelease) |
| 35 | return QObject::eventFilter(obj, event); | 35 | return QObject::eventFilter(obj, event); |
| @@ -88,29 +88,21 @@ bool GameList::SearchField::KeyReleaseEater::eventFilter(QObject* obj, QEvent* e | |||
| 88 | return QObject::eventFilter(obj, event); | 88 | return QObject::eventFilter(obj, event); |
| 89 | } | 89 | } |
| 90 | 90 | ||
| 91 | void GameList::SearchField::setFilterResult(int visible, int total) { | 91 | void GameListSearchField::setFilterResult(int visible, int total) { |
| 92 | QString result_of_text = tr("of"); | 92 | label_filter_result->setText(tr("%1 of %n result(s)", "", total).arg(visible)); |
| 93 | QString result_text; | ||
| 94 | if (total == 1) { | ||
| 95 | result_text = tr("result"); | ||
| 96 | } else { | ||
| 97 | result_text = tr("results"); | ||
| 98 | } | ||
| 99 | label_filter_result->setText( | ||
| 100 | QString("%1 %2 %3 %4").arg(visible).arg(result_of_text).arg(total).arg(result_text)); | ||
| 101 | } | 93 | } |
| 102 | 94 | ||
| 103 | void GameList::SearchField::clear() { | 95 | void GameListSearchField::clear() { |
| 104 | edit_filter->setText(""); | 96 | edit_filter->setText(""); |
| 105 | } | 97 | } |
| 106 | 98 | ||
| 107 | void GameList::SearchField::setFocus() { | 99 | void GameListSearchField::setFocus() { |
| 108 | if (edit_filter->isVisible()) { | 100 | if (edit_filter->isVisible()) { |
| 109 | edit_filter->setFocus(); | 101 | edit_filter->setFocus(); |
| 110 | } | 102 | } |
| 111 | } | 103 | } |
| 112 | 104 | ||
| 113 | GameList::SearchField::SearchField(GameList* parent) : QWidget{parent} { | 105 | GameListSearchField::GameListSearchField(GameList* parent) : QWidget{parent} { |
| 114 | KeyReleaseEater* keyReleaseEater = new KeyReleaseEater(parent); | 106 | KeyReleaseEater* keyReleaseEater = new KeyReleaseEater(parent); |
| 115 | layout_filter = new QHBoxLayout; | 107 | layout_filter = new QHBoxLayout; |
| 116 | layout_filter->setMargin(8); | 108 | layout_filter->setMargin(8); |
| @@ -210,7 +202,7 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent) | |||
| 210 | this->main_window = parent; | 202 | this->main_window = parent; |
| 211 | layout = new QVBoxLayout; | 203 | layout = new QVBoxLayout; |
| 212 | tree_view = new QTreeView; | 204 | tree_view = new QTreeView; |
| 213 | search_field = new SearchField(this); | 205 | search_field = new GameListSearchField(this); |
| 214 | item_model = new QStandardItemModel(tree_view); | 206 | item_model = new QStandardItemModel(tree_view); |
| 215 | tree_view->setModel(item_model); | 207 | tree_view->setModel(item_model); |
| 216 | 208 | ||
| @@ -326,9 +318,14 @@ void GameList::PopupContextMenu(const QPoint& menu_location) { | |||
| 326 | int row = item_model->itemFromIndex(item)->row(); | 318 | int row = item_model->itemFromIndex(item)->row(); |
| 327 | QStandardItem* child_file = item_model->invisibleRootItem()->child(row, COLUMN_NAME); | 319 | QStandardItem* child_file = item_model->invisibleRootItem()->child(row, COLUMN_NAME); |
| 328 | u64 program_id = child_file->data(GameListItemPath::ProgramIdRole).toULongLong(); | 320 | u64 program_id = child_file->data(GameListItemPath::ProgramIdRole).toULongLong(); |
| 321 | std::string path = child_file->data(GameListItemPath::FullPathRole).toString().toStdString(); | ||
| 329 | 322 | ||
| 330 | QMenu context_menu; | 323 | QMenu context_menu; |
| 331 | QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location")); | 324 | QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location")); |
| 325 | QAction* open_lfs_location = context_menu.addAction(tr("Open Mod Data Location")); | ||
| 326 | context_menu.addSeparator(); | ||
| 327 | QAction* dump_romfs = context_menu.addAction(tr("Dump RomFS")); | ||
| 328 | QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); | ||
| 332 | QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); | 329 | QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); |
| 333 | 330 | ||
| 334 | open_save_location->setEnabled(program_id != 0); | 331 | open_save_location->setEnabled(program_id != 0); |
| @@ -337,6 +334,10 @@ void GameList::PopupContextMenu(const QPoint& menu_location) { | |||
| 337 | 334 | ||
| 338 | connect(open_save_location, &QAction::triggered, | 335 | connect(open_save_location, &QAction::triggered, |
| 339 | [&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData); }); | 336 | [&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData); }); |
| 337 | connect(open_lfs_location, &QAction::triggered, | ||
| 338 | [&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::ModData); }); | ||
| 339 | connect(dump_romfs, &QAction::triggered, [&]() { emit DumpRomFSRequested(program_id, path); }); | ||
| 340 | connect(copy_tid, &QAction::triggered, [&]() { emit CopyTIDRequested(program_id); }); | ||
| 340 | connect(navigate_to_gamedb_entry, &QAction::triggered, | 341 | connect(navigate_to_gamedb_entry, &QAction::triggered, |
| 341 | [&]() { emit NavigateToGamedbEntryRequested(program_id, compatibility_list); }); | 342 | [&]() { emit NavigateToGamedbEntryRequested(program_id, compatibility_list); }); |
| 342 | 343 | ||
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index 2713e7b54..05e115e19 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h | |||
| @@ -22,13 +22,17 @@ | |||
| 22 | #include "yuzu/compatibility_list.h" | 22 | #include "yuzu/compatibility_list.h" |
| 23 | 23 | ||
| 24 | class GameListWorker; | 24 | class GameListWorker; |
| 25 | class GameListSearchField; | ||
| 25 | class GMainWindow; | 26 | class GMainWindow; |
| 26 | 27 | ||
| 27 | namespace FileSys { | 28 | namespace FileSys { |
| 28 | class VfsFilesystem; | 29 | class VfsFilesystem; |
| 29 | } | 30 | } |
| 30 | 31 | ||
| 31 | enum class GameListOpenTarget { SaveData }; | 32 | enum class GameListOpenTarget { |
| 33 | SaveData, | ||
| 34 | ModData, | ||
| 35 | }; | ||
| 32 | 36 | ||
| 33 | class GameList : public QWidget { | 37 | class GameList : public QWidget { |
| 34 | Q_OBJECT | 38 | Q_OBJECT |
| @@ -43,33 +47,6 @@ public: | |||
| 43 | COLUMN_COUNT, // Number of columns | 47 | COLUMN_COUNT, // Number of columns |
| 44 | }; | 48 | }; |
| 45 | 49 | ||
| 46 | class SearchField : public QWidget { | ||
| 47 | public: | ||
| 48 | void setFilterResult(int visible, int total); | ||
| 49 | void clear(); | ||
| 50 | void setFocus(); | ||
| 51 | explicit SearchField(GameList* parent = nullptr); | ||
| 52 | |||
| 53 | private: | ||
| 54 | class KeyReleaseEater : public QObject { | ||
| 55 | public: | ||
| 56 | explicit KeyReleaseEater(GameList* gamelist); | ||
| 57 | |||
| 58 | private: | ||
| 59 | GameList* gamelist = nullptr; | ||
| 60 | QString edit_filter_text_old; | ||
| 61 | |||
| 62 | protected: | ||
| 63 | bool eventFilter(QObject* obj, QEvent* event) override; | ||
| 64 | }; | ||
| 65 | QHBoxLayout* layout_filter = nullptr; | ||
| 66 | QTreeView* tree_view = nullptr; | ||
| 67 | QLabel* label_filter = nullptr; | ||
| 68 | QLineEdit* edit_filter = nullptr; | ||
| 69 | QLabel* label_filter_result = nullptr; | ||
| 70 | QToolButton* button_filter_close = nullptr; | ||
| 71 | }; | ||
| 72 | |||
| 73 | explicit GameList(std::shared_ptr<FileSys::VfsFilesystem> vfs, GMainWindow* parent = nullptr); | 50 | explicit GameList(std::shared_ptr<FileSys::VfsFilesystem> vfs, GMainWindow* parent = nullptr); |
| 74 | ~GameList() override; | 51 | ~GameList() override; |
| 75 | 52 | ||
| @@ -89,6 +66,8 @@ signals: | |||
| 89 | void GameChosen(QString game_path); | 66 | void GameChosen(QString game_path); |
| 90 | void ShouldCancelWorker(); | 67 | void ShouldCancelWorker(); |
| 91 | void OpenFolderRequested(u64 program_id, GameListOpenTarget target); | 68 | void OpenFolderRequested(u64 program_id, GameListOpenTarget target); |
| 69 | void DumpRomFSRequested(u64 program_id, const std::string& game_path); | ||
| 70 | void CopyTIDRequested(u64 program_id); | ||
| 92 | void NavigateToGamedbEntryRequested(u64 program_id, | 71 | void NavigateToGamedbEntryRequested(u64 program_id, |
| 93 | const CompatibilityList& compatibility_list); | 72 | const CompatibilityList& compatibility_list); |
| 94 | 73 | ||
| @@ -105,7 +84,7 @@ private: | |||
| 105 | void RefreshGameDirectory(); | 84 | void RefreshGameDirectory(); |
| 106 | 85 | ||
| 107 | std::shared_ptr<FileSys::VfsFilesystem> vfs; | 86 | std::shared_ptr<FileSys::VfsFilesystem> vfs; |
| 108 | SearchField* search_field; | 87 | GameListSearchField* search_field; |
| 109 | GMainWindow* main_window = nullptr; | 88 | GMainWindow* main_window = nullptr; |
| 110 | QVBoxLayout* layout = nullptr; | 89 | QVBoxLayout* layout = nullptr; |
| 111 | QTreeView* tree_view = nullptr; | 90 | QTreeView* tree_view = nullptr; |
| @@ -113,6 +92,8 @@ private: | |||
| 113 | GameListWorker* current_worker = nullptr; | 92 | GameListWorker* current_worker = nullptr; |
| 114 | QFileSystemWatcher* watcher = nullptr; | 93 | QFileSystemWatcher* watcher = nullptr; |
| 115 | CompatibilityList compatibility_list; | 94 | CompatibilityList compatibility_list; |
| 95 | |||
| 96 | friend class GameListSearchField; | ||
| 116 | }; | 97 | }; |
| 117 | 98 | ||
| 118 | Q_DECLARE_METATYPE(GameListOpenTarget); | 99 | Q_DECLARE_METATYPE(GameListOpenTarget); |
diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h index f22e422e5..3db0e90da 100644 --- a/src/yuzu/game_list_p.h +++ b/src/yuzu/game_list_p.h | |||
| @@ -16,6 +16,7 @@ | |||
| 16 | #include <QObject> | 16 | #include <QObject> |
| 17 | #include <QStandardItem> | 17 | #include <QStandardItem> |
| 18 | #include <QString> | 18 | #include <QString> |
| 19 | #include <QWidget> | ||
| 19 | 20 | ||
| 20 | #include "common/common_types.h" | 21 | #include "common/common_types.h" |
| 21 | #include "common/logging/log.h" | 22 | #include "common/logging/log.h" |
| @@ -68,7 +69,7 @@ public: | |||
| 68 | if (!picture.loadFromData(picture_data.data(), static_cast<u32>(picture_data.size()))) { | 69 | if (!picture.loadFromData(picture_data.data(), static_cast<u32>(picture_data.size()))) { |
| 69 | picture = GetDefaultIcon(size); | 70 | picture = GetDefaultIcon(size); |
| 70 | } | 71 | } |
| 71 | picture = picture.scaled(size, size); | 72 | picture = picture.scaled(size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); |
| 72 | 73 | ||
| 73 | setData(picture, Qt::DecorationRole); | 74 | setData(picture, Qt::DecorationRole); |
| 74 | } | 75 | } |
| @@ -106,7 +107,7 @@ class GameListItemCompat : public GameListItem { | |||
| 106 | public: | 107 | public: |
| 107 | static const int CompatNumberRole = Qt::UserRole + 1; | 108 | static const int CompatNumberRole = Qt::UserRole + 1; |
| 108 | GameListItemCompat() = default; | 109 | GameListItemCompat() = default; |
| 109 | explicit GameListItemCompat(const QString& compatiblity) { | 110 | explicit GameListItemCompat(const QString& compatibility) { |
| 110 | struct CompatStatus { | 111 | struct CompatStatus { |
| 111 | QString color; | 112 | QString color; |
| 112 | const char* text; | 113 | const char* text; |
| @@ -123,13 +124,13 @@ public: | |||
| 123 | {"99", {"#000000", QT_TR_NOOP("Not Tested"), QT_TR_NOOP("The game has not yet been tested.")}}}; | 124 | {"99", {"#000000", QT_TR_NOOP("Not Tested"), QT_TR_NOOP("The game has not yet been tested.")}}}; |
| 124 | // clang-format on | 125 | // clang-format on |
| 125 | 126 | ||
| 126 | auto iterator = status_data.find(compatiblity); | 127 | auto iterator = status_data.find(compatibility); |
| 127 | if (iterator == status_data.end()) { | 128 | if (iterator == status_data.end()) { |
| 128 | LOG_WARNING(Frontend, "Invalid compatibility number {}", compatiblity.toStdString()); | 129 | LOG_WARNING(Frontend, "Invalid compatibility number {}", compatibility.toStdString()); |
| 129 | return; | 130 | return; |
| 130 | } | 131 | } |
| 131 | CompatStatus status = iterator->second; | 132 | const CompatStatus& status = iterator->second; |
| 132 | setData(compatiblity, CompatNumberRole); | 133 | setData(compatibility, CompatNumberRole); |
| 133 | setText(QObject::tr(status.text)); | 134 | setText(QObject::tr(status.text)); |
| 134 | setToolTip(QObject::tr(status.tooltip)); | 135 | setToolTip(QObject::tr(status.tooltip)); |
| 135 | setData(CreateCirclePixmapFromColor(status.color), Qt::DecorationRole); | 136 | setData(CreateCirclePixmapFromColor(status.color), Qt::DecorationRole); |
| @@ -176,3 +177,42 @@ public: | |||
| 176 | return data(SizeRole).toULongLong() < other.data(SizeRole).toULongLong(); | 177 | return data(SizeRole).toULongLong() < other.data(SizeRole).toULongLong(); |
| 177 | } | 178 | } |
| 178 | }; | 179 | }; |
| 180 | |||
| 181 | class GameList; | ||
| 182 | class QHBoxLayout; | ||
| 183 | class QTreeView; | ||
| 184 | class QLabel; | ||
| 185 | class QLineEdit; | ||
| 186 | class QToolButton; | ||
| 187 | |||
| 188 | class GameListSearchField : public QWidget { | ||
| 189 | Q_OBJECT | ||
| 190 | |||
| 191 | public: | ||
| 192 | explicit GameListSearchField(GameList* parent = nullptr); | ||
| 193 | |||
| 194 | void setFilterResult(int visible, int total); | ||
| 195 | |||
| 196 | void clear(); | ||
| 197 | void setFocus(); | ||
| 198 | |||
| 199 | private: | ||
| 200 | class KeyReleaseEater : public QObject { | ||
| 201 | public: | ||
| 202 | explicit KeyReleaseEater(GameList* gamelist); | ||
| 203 | |||
| 204 | private: | ||
| 205 | GameList* gamelist = nullptr; | ||
| 206 | QString edit_filter_text_old; | ||
| 207 | |||
| 208 | protected: | ||
| 209 | // EventFilter in order to process systemkeys while editing the searchfield | ||
| 210 | bool eventFilter(QObject* obj, QEvent* event) override; | ||
| 211 | }; | ||
| 212 | QHBoxLayout* layout_filter = nullptr; | ||
| 213 | QTreeView* tree_view = nullptr; | ||
| 214 | QLabel* label_filter = nullptr; | ||
| 215 | QLineEdit* edit_filter = nullptr; | ||
| 216 | QLabel* label_filter_result = nullptr; | ||
| 217 | QToolButton* button_filter_close = nullptr; | ||
| 218 | }; | ||
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 22a317737..681758ad2 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -7,6 +7,22 @@ | |||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include <thread> | 8 | #include <thread> |
| 9 | 9 | ||
| 10 | // VFS includes must be before glad as they will conflict with Windows file api, which uses defines. | ||
| 11 | #include "core/file_sys/vfs.h" | ||
| 12 | #include "core/file_sys/vfs_real.h" | ||
| 13 | |||
| 14 | // These are wrappers to avoid the calls to CreateDirectory and CreateFile becuase of the Windows | ||
| 15 | // defines. | ||
| 16 | static FileSys::VirtualDir VfsFilesystemCreateDirectoryWrapper( | ||
| 17 | const FileSys::VirtualFilesystem& vfs, const std::string& path, FileSys::Mode mode) { | ||
| 18 | return vfs->CreateDirectory(path, mode); | ||
| 19 | } | ||
| 20 | |||
| 21 | static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::VirtualDir& dir, | ||
| 22 | const std::string& path) { | ||
| 23 | return dir->CreateFile(path); | ||
| 24 | } | ||
| 25 | |||
| 10 | #include <fmt/ostream.h> | 26 | #include <fmt/ostream.h> |
| 11 | #include <glad/glad.h> | 27 | #include <glad/glad.h> |
| 12 | 28 | ||
| @@ -30,16 +46,18 @@ | |||
| 30 | #include "common/telemetry.h" | 46 | #include "common/telemetry.h" |
| 31 | #include "core/core.h" | 47 | #include "core/core.h" |
| 32 | #include "core/crypto/key_manager.h" | 48 | #include "core/crypto/key_manager.h" |
| 49 | #include "core/file_sys/bis_factory.h" | ||
| 33 | #include "core/file_sys/card_image.h" | 50 | #include "core/file_sys/card_image.h" |
| 34 | #include "core/file_sys/content_archive.h" | 51 | #include "core/file_sys/content_archive.h" |
| 35 | #include "core/file_sys/control_metadata.h" | 52 | #include "core/file_sys/control_metadata.h" |
| 36 | #include "core/file_sys/patch_manager.h" | 53 | #include "core/file_sys/patch_manager.h" |
| 37 | #include "core/file_sys/registered_cache.h" | 54 | #include "core/file_sys/registered_cache.h" |
| 55 | #include "core/file_sys/romfs.h" | ||
| 38 | #include "core/file_sys/savedata_factory.h" | 56 | #include "core/file_sys/savedata_factory.h" |
| 39 | #include "core/file_sys/submission_package.h" | 57 | #include "core/file_sys/submission_package.h" |
| 40 | #include "core/file_sys/vfs_real.h" | ||
| 41 | #include "core/hle/kernel/process.h" | 58 | #include "core/hle/kernel/process.h" |
| 42 | #include "core/hle/service/filesystem/filesystem.h" | 59 | #include "core/hle/service/filesystem/filesystem.h" |
| 60 | #include "core/hle/service/filesystem/fsp_ldr.h" | ||
| 43 | #include "core/loader/loader.h" | 61 | #include "core/loader/loader.h" |
| 44 | #include "core/perf_stats.h" | 62 | #include "core/perf_stats.h" |
| 45 | #include "core/settings.h" | 63 | #include "core/settings.h" |
| @@ -362,6 +380,8 @@ void GMainWindow::RestoreUIState() { | |||
| 362 | void GMainWindow::ConnectWidgetEvents() { | 380 | void GMainWindow::ConnectWidgetEvents() { |
| 363 | connect(game_list, &GameList::GameChosen, this, &GMainWindow::OnGameListLoadFile); | 381 | connect(game_list, &GameList::GameChosen, this, &GMainWindow::OnGameListLoadFile); |
| 364 | connect(game_list, &GameList::OpenFolderRequested, this, &GMainWindow::OnGameListOpenFolder); | 382 | connect(game_list, &GameList::OpenFolderRequested, this, &GMainWindow::OnGameListOpenFolder); |
| 383 | connect(game_list, &GameList::DumpRomFSRequested, this, &GMainWindow::OnGameListDumpRomFS); | ||
| 384 | connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID); | ||
| 365 | connect(game_list, &GameList::NavigateToGamedbEntryRequested, this, | 385 | connect(game_list, &GameList::NavigateToGamedbEntryRequested, this, |
| 366 | &GMainWindow::OnGameListNavigateToGamedbEntry); | 386 | &GMainWindow::OnGameListNavigateToGamedbEntry); |
| 367 | 387 | ||
| @@ -602,9 +622,9 @@ void GMainWindow::BootGame(const QString& filename) { | |||
| 602 | std::string title_name; | 622 | std::string title_name; |
| 603 | const auto res = Core::System::GetInstance().GetGameName(title_name); | 623 | const auto res = Core::System::GetInstance().GetGameName(title_name); |
| 604 | if (res != Loader::ResultStatus::Success) { | 624 | if (res != Loader::ResultStatus::Success) { |
| 605 | const u64 program_id = Core::System::GetInstance().CurrentProcess()->program_id; | 625 | const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID(); |
| 606 | 626 | ||
| 607 | const auto [nacp, icon_file] = FileSys::PatchManager(program_id).GetControlMetadata(); | 627 | const auto [nacp, icon_file] = FileSys::PatchManager(title_id).GetControlMetadata(); |
| 608 | if (nacp != nullptr) | 628 | if (nacp != nullptr) |
| 609 | title_name = nacp->GetApplicationName(); | 629 | title_name = nacp->GetApplicationName(); |
| 610 | 630 | ||
| @@ -713,6 +733,12 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target | |||
| 713 | program_id, user_id, 0); | 733 | program_id, user_id, 0); |
| 714 | break; | 734 | break; |
| 715 | } | 735 | } |
| 736 | case GameListOpenTarget::ModData: { | ||
| 737 | open_target = "Mod Data"; | ||
| 738 | const auto load_dir = FileUtil::GetUserPath(FileUtil::UserPath::LoadDir); | ||
| 739 | path = fmt::format("{}{:016X}", load_dir, program_id); | ||
| 740 | break; | ||
| 741 | } | ||
| 716 | default: | 742 | default: |
| 717 | UNIMPLEMENTED(); | 743 | UNIMPLEMENTED(); |
| 718 | } | 744 | } |
| @@ -730,6 +756,120 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target | |||
| 730 | QDesktopServices::openUrl(QUrl::fromLocalFile(qpath)); | 756 | QDesktopServices::openUrl(QUrl::fromLocalFile(qpath)); |
| 731 | } | 757 | } |
| 732 | 758 | ||
| 759 | static std::size_t CalculateRomFSEntrySize(const FileSys::VirtualDir& dir, bool full) { | ||
| 760 | std::size_t out = 0; | ||
| 761 | |||
| 762 | for (const auto& subdir : dir->GetSubdirectories()) { | ||
| 763 | out += 1 + CalculateRomFSEntrySize(subdir, full); | ||
| 764 | } | ||
| 765 | |||
| 766 | return out + (full ? dir->GetFiles().size() : 0); | ||
| 767 | } | ||
| 768 | |||
| 769 | static bool RomFSRawCopy(QProgressDialog& dialog, const FileSys::VirtualDir& src, | ||
| 770 | const FileSys::VirtualDir& dest, std::size_t block_size, bool full) { | ||
| 771 | if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable()) | ||
| 772 | return false; | ||
| 773 | if (dialog.wasCanceled()) | ||
| 774 | return false; | ||
| 775 | |||
| 776 | if (full) { | ||
| 777 | for (const auto& file : src->GetFiles()) { | ||
| 778 | const auto out = VfsDirectoryCreateFileWrapper(dest, file->GetName()); | ||
| 779 | if (!FileSys::VfsRawCopy(file, out, block_size)) | ||
| 780 | return false; | ||
| 781 | dialog.setValue(dialog.value() + 1); | ||
| 782 | if (dialog.wasCanceled()) | ||
| 783 | return false; | ||
| 784 | } | ||
| 785 | } | ||
| 786 | |||
| 787 | for (const auto& dir : src->GetSubdirectories()) { | ||
| 788 | const auto out = dest->CreateSubdirectory(dir->GetName()); | ||
| 789 | if (!RomFSRawCopy(dialog, dir, out, block_size, full)) | ||
| 790 | return false; | ||
| 791 | dialog.setValue(dialog.value() + 1); | ||
| 792 | if (dialog.wasCanceled()) | ||
| 793 | return false; | ||
| 794 | } | ||
| 795 | |||
| 796 | return true; | ||
| 797 | } | ||
| 798 | |||
| 799 | void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) { | ||
| 800 | const auto path = fmt::format("{}{:016X}/romfs", | ||
| 801 | FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), program_id); | ||
| 802 | |||
| 803 | const auto failed = [this, &path] { | ||
| 804 | QMessageBox::warning(this, tr("RomFS Extraction Failed!"), | ||
| 805 | tr("There was an error copying the RomFS files or the user " | ||
| 806 | "cancelled the operation.")); | ||
| 807 | vfs->DeleteDirectory(path); | ||
| 808 | }; | ||
| 809 | |||
| 810 | const auto loader = Loader::GetLoader(vfs->OpenFile(game_path, FileSys::Mode::Read)); | ||
| 811 | if (loader == nullptr) { | ||
| 812 | failed(); | ||
| 813 | return; | ||
| 814 | } | ||
| 815 | |||
| 816 | FileSys::VirtualFile file; | ||
| 817 | if (loader->ReadRomFS(file) != Loader::ResultStatus::Success) { | ||
| 818 | failed(); | ||
| 819 | return; | ||
| 820 | } | ||
| 821 | |||
| 822 | const auto romfs = | ||
| 823 | loader->IsRomFSUpdatable() | ||
| 824 | ? FileSys::PatchManager(program_id).PatchRomFS(file, loader->ReadRomFSIVFCOffset()) | ||
| 825 | : file; | ||
| 826 | |||
| 827 | const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full); | ||
| 828 | if (extracted == nullptr) { | ||
| 829 | failed(); | ||
| 830 | return; | ||
| 831 | } | ||
| 832 | |||
| 833 | const auto out = VfsFilesystemCreateDirectoryWrapper(vfs, path, FileSys::Mode::ReadWrite); | ||
| 834 | |||
| 835 | if (out == nullptr) { | ||
| 836 | failed(); | ||
| 837 | return; | ||
| 838 | } | ||
| 839 | |||
| 840 | bool ok; | ||
| 841 | const auto res = QInputDialog::getItem( | ||
| 842 | this, tr("Select RomFS Dump Mode"), | ||
| 843 | tr("Please select the how you would like the RomFS dumped.<br>Full will copy all of the " | ||
| 844 | "files into the new directory while <br>skeleton will only create the directory " | ||
| 845 | "structure."), | ||
| 846 | {"Full", "Skeleton"}, 0, false, &ok); | ||
| 847 | if (!ok) | ||
| 848 | failed(); | ||
| 849 | |||
| 850 | const auto full = res == "Full"; | ||
| 851 | const auto entry_size = CalculateRomFSEntrySize(extracted, full); | ||
| 852 | |||
| 853 | QProgressDialog progress(tr("Extracting RomFS..."), tr("Cancel"), 0, entry_size, this); | ||
| 854 | progress.setWindowModality(Qt::WindowModal); | ||
| 855 | progress.setMinimumDuration(100); | ||
| 856 | |||
| 857 | if (RomFSRawCopy(progress, extracted, out, 0x400000, full)) { | ||
| 858 | progress.close(); | ||
| 859 | QMessageBox::information(this, tr("RomFS Extraction Succeeded!"), | ||
| 860 | tr("The operation completed successfully.")); | ||
| 861 | QDesktopServices::openUrl(QUrl::fromLocalFile(QString::fromStdString(path))); | ||
| 862 | } else { | ||
| 863 | progress.close(); | ||
| 864 | failed(); | ||
| 865 | } | ||
| 866 | } | ||
| 867 | |||
| 868 | void GMainWindow::OnGameListCopyTID(u64 program_id) { | ||
| 869 | QClipboard* clipboard = QGuiApplication::clipboard(); | ||
| 870 | clipboard->setText(QString::fromStdString(fmt::format("{:016X}", program_id))); | ||
| 871 | } | ||
| 872 | |||
| 733 | void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id, | 873 | void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id, |
| 734 | const CompatibilityList& compatibility_list) { | 874 | const CompatibilityList& compatibility_list) { |
| 735 | const auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); | 875 | const auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); |
| @@ -790,7 +930,8 @@ void GMainWindow::OnMenuInstallToNAND() { | |||
| 790 | return; | 930 | return; |
| 791 | } | 931 | } |
| 792 | 932 | ||
| 793 | const auto qt_raw_copy = [this](FileSys::VirtualFile src, FileSys::VirtualFile dest) { | 933 | const auto qt_raw_copy = [this](const FileSys::VirtualFile& src, |
| 934 | const FileSys::VirtualFile& dest, std::size_t block_size) { | ||
| 794 | if (src == nullptr || dest == nullptr) | 935 | if (src == nullptr || dest == nullptr) |
| 795 | return false; | 936 | return false; |
| 796 | if (!dest->Resize(src->GetSize())) | 937 | if (!dest->Resize(src->GetSize())) |
| @@ -804,7 +945,7 @@ void GMainWindow::OnMenuInstallToNAND() { | |||
| 804 | tr("Cancel"), 0, progress_maximum, this); | 945 | tr("Cancel"), 0, progress_maximum, this); |
| 805 | progress.setWindowModality(Qt::WindowModal); | 946 | progress.setWindowModality(Qt::WindowModal); |
| 806 | 947 | ||
| 807 | for (size_t i = 0; i < src->GetSize(); i += buffer.size()) { | 948 | for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) { |
| 808 | if (progress.wasCanceled()) { | 949 | if (progress.wasCanceled()) { |
| 809 | dest->Resize(0); | 950 | dest->Resize(0); |
| 810 | return false; | 951 | return false; |
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 552e3e61c..8ee9242b1 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -138,6 +138,8 @@ private slots: | |||
| 138 | /// Called whenever a user selects a game in the game list widget. | 138 | /// Called whenever a user selects a game in the game list widget. |
| 139 | void OnGameListLoadFile(QString game_path); | 139 | void OnGameListLoadFile(QString game_path); |
| 140 | void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target); | 140 | void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target); |
| 141 | void OnGameListDumpRomFS(u64 program_id, const std::string& game_path); | ||
| 142 | void OnGameListCopyTID(u64 program_id); | ||
| 141 | void OnGameListNavigateToGamedbEntry(u64 program_id, | 143 | void OnGameListNavigateToGamedbEntry(u64 program_id, |
| 142 | const CompatibilityList& compatibility_list); | 144 | const CompatibilityList& compatibility_list); |
| 143 | void OnMenuLoadFile(); | 145 | void OnMenuLoadFile(); |
diff --git a/src/yuzu/util/util.cpp b/src/yuzu/util/util.cpp index e99042a23..62c080aff 100644 --- a/src/yuzu/util/util.cpp +++ b/src/yuzu/util/util.cpp | |||
| @@ -30,8 +30,9 @@ QPixmap CreateCirclePixmapFromColor(const QColor& color) { | |||
| 30 | QPixmap circle_pixmap(16, 16); | 30 | QPixmap circle_pixmap(16, 16); |
| 31 | circle_pixmap.fill(Qt::transparent); | 31 | circle_pixmap.fill(Qt::transparent); |
| 32 | QPainter painter(&circle_pixmap); | 32 | QPainter painter(&circle_pixmap); |
| 33 | painter.setRenderHint(QPainter::Antialiasing); | ||
| 33 | painter.setPen(color); | 34 | painter.setPen(color); |
| 34 | painter.setBrush(color); | 35 | painter.setBrush(color); |
| 35 | painter.drawEllipse(0, 0, 15, 15); | 36 | painter.drawEllipse({circle_pixmap.width() / 2.0, circle_pixmap.height() / 2.0}, 7.0, 7.0); |
| 36 | return circle_pixmap; | 37 | return circle_pixmap; |
| 37 | } | 38 | } |
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index 991abda2e..a478b0a56 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp | |||
| @@ -120,11 +120,15 @@ void Config::ReadValues() { | |||
| 120 | sdl2_config->Get("Data Storage", "nand_directory", | 120 | sdl2_config->Get("Data Storage", "nand_directory", |
| 121 | FileUtil::GetUserPath(FileUtil::UserPath::NANDDir))); | 121 | FileUtil::GetUserPath(FileUtil::UserPath::NANDDir))); |
| 122 | FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir, | 122 | FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir, |
| 123 | sdl2_config->Get("Data Storage", "nand_directory", | 123 | sdl2_config->Get("Data Storage", "sdmc_directory", |
| 124 | FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))); | 124 | FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))); |
| 125 | 125 | ||
| 126 | // System | 126 | // System |
| 127 | Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false); | 127 | Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false); |
| 128 | Settings::values.username = sdl2_config->Get("System", "username", "yuzu"); | ||
| 129 | if (Settings::values.username.empty()) { | ||
| 130 | Settings::values.username = "yuzu"; | ||
| 131 | } | ||
| 128 | 132 | ||
| 129 | // Miscellaneous | 133 | // Miscellaneous |
| 130 | Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace"); | 134 | Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace"); |
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index 002a4ec15..d35c441e9 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h | |||
| @@ -176,7 +176,7 @@ use_docked_mode = | |||
| 176 | 176 | ||
| 177 | # Sets the account username, max length is 32 characters | 177 | # Sets the account username, max length is 32 characters |
| 178 | # yuzu (default) | 178 | # yuzu (default) |
| 179 | username = | 179 | username = yuzu |
| 180 | 180 | ||
| 181 | # Sets the systems language index | 181 | # Sets the systems language index |
| 182 | # 0: Japanese, 1: English (default), 2: French, 3: German, 4: Italian, 5: Spanish, 6: Chinese, | 182 | # 0: Japanese, 1: English (default), 2: French, 3: German, 4: Italian, 5: Spanish, 6: Chinese, |
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index b1c364fbb..b2559b717 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp | |||
| @@ -20,8 +20,10 @@ | |||
| 20 | #include "common/string_util.h" | 20 | #include "common/string_util.h" |
| 21 | #include "common/telemetry.h" | 21 | #include "common/telemetry.h" |
| 22 | #include "core/core.h" | 22 | #include "core/core.h" |
| 23 | #include "core/crypto/key_manager.h" | ||
| 23 | #include "core/file_sys/vfs_real.h" | 24 | #include "core/file_sys/vfs_real.h" |
| 24 | #include "core/gdbstub/gdbstub.h" | 25 | #include "core/gdbstub/gdbstub.h" |
| 26 | #include "core/hle/service/filesystem/filesystem.h" | ||
| 25 | #include "core/loader/loader.h" | 27 | #include "core/loader/loader.h" |
| 26 | #include "core/settings.h" | 28 | #include "core/settings.h" |
| 27 | #include "core/telemetry_session.h" | 29 | #include "core/telemetry_session.h" |
| @@ -29,7 +31,6 @@ | |||
| 29 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" | 31 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" |
| 30 | 32 | ||
| 31 | #include <getopt.h> | 33 | #include <getopt.h> |
| 32 | #include "core/crypto/key_manager.h" | ||
| 33 | #ifndef _MSC_VER | 34 | #ifndef _MSC_VER |
| 34 | #include <unistd.h> | 35 | #include <unistd.h> |
| 35 | #endif | 36 | #endif |
| @@ -169,6 +170,7 @@ int main(int argc, char** argv) { | |||
| 169 | 170 | ||
| 170 | Core::System& system{Core::System::GetInstance()}; | 171 | Core::System& system{Core::System::GetInstance()}; |
| 171 | system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); | 172 | system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); |
| 173 | Service::FileSystem::CreateFactories(system.GetFilesystem()); | ||
| 172 | 174 | ||
| 173 | SCOPE_EXIT({ system.Shutdown(); }); | 175 | SCOPE_EXIT({ system.Shutdown(); }); |
| 174 | 176 | ||