summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitattributes4
-rw-r--r--.travis.yml15
-rwxr-xr-x.travis/common/post-upload.sh3
-rwxr-xr-x.travis/linux-mingw/build.sh3
-rwxr-xr-x.travis/linux-mingw/deps.sh3
-rwxr-xr-x.travis/linux-mingw/docker.sh59
-rw-r--r--.travis/linux-mingw/scan_dll.py106
-rwxr-xr-x.travis/linux-mingw/upload.sh13
-rwxr-xr-x.travis/linux/docker.sh4
-rwxr-xr-x.travis/macos/build.sh6
-rw-r--r--CMakeLists.txt27
-rw-r--r--CMakeModules/MinGWCross.cmake54
m---------externals/dynarmic0
-rw-r--r--src/audio_core/algorithm/filter.cpp12
-rw-r--r--src/audio_core/algorithm/filter.h4
-rw-r--r--src/audio_core/algorithm/interpolate.cpp12
-rw-r--r--src/audio_core/algorithm/interpolate.h4
-rw-r--r--src/audio_core/audio_out.cpp3
-rw-r--r--src/audio_core/audio_out.h2
-rw-r--r--src/audio_core/audio_renderer.cpp68
-rw-r--r--src/audio_core/audio_renderer.h52
-rw-r--r--src/audio_core/codec.cpp20
-rw-r--r--src/audio_core/codec.h2
-rw-r--r--src/audio_core/cubeb_sink.cpp31
-rw-r--r--src/audio_core/null_sink.h2
-rw-r--r--src/audio_core/stream.cpp14
-rw-r--r--src/audio_core/stream.h28
-rw-r--r--src/audio_core/time_stretch.cpp9
-rw-r--r--src/audio_core/time_stretch.h3
-rw-r--r--src/common/alignment.h4
-rw-r--r--src/common/bit_field.h4
-rw-r--r--src/common/bit_set.h6
-rw-r--r--src/common/cityhash.cpp22
-rw-r--r--src/common/cityhash.h12
-rw-r--r--src/common/common_paths.h2
-rw-r--r--src/common/file_util.cpp18
-rw-r--r--src/common/file_util.h27
-rw-r--r--src/common/hash.h4
-rw-r--r--src/common/hex_util.cpp4
-rw-r--r--src/common/hex_util.h12
-rw-r--r--src/common/logging/backend.cpp3
-rw-r--r--src/common/logging/backend.h2
-rw-r--r--src/common/logging/filter.cpp5
-rw-r--r--src/common/logging/filter.h2
-rw-r--r--src/common/logging/log.h1
-rw-r--r--src/common/memory_util.cpp12
-rw-r--r--src/common/memory_util.h12
-rw-r--r--src/common/misc.cpp2
-rw-r--r--src/common/ring_buffer.h58
-rw-r--r--src/common/string_util.cpp42
-rw-r--r--src/common/string_util.h4
-rw-r--r--src/common/thread.h18
-rw-r--r--src/common/x64/xbyak_abi.h21
-rw-r--r--src/common/x64/xbyak_util.h2
-rw-r--r--src/core/CMakeLists.txt5
-rw-r--r--src/core/arm/arm_interface.h68
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp86
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.h36
-rw-r--r--src/core/arm/exclusive_monitor.h12
-rw-r--r--src/core/arm/unicorn/arm_unicorn.cpp31
-rw-r--r--src/core/arm/unicorn/arm_unicorn.h14
-rw-r--r--src/core/core.cpp18
-rw-r--r--src/core/core.h8
-rw-r--r--src/core/core_cpu.cpp15
-rw-r--r--src/core/core_cpu.h15
-rw-r--r--src/core/crypto/aes_util.cpp37
-rw-r--r--src/core/crypto/aes_util.h14
-rw-r--r--src/core/crypto/ctr_encryption_layer.cpp11
-rw-r--r--src/core/crypto/ctr_encryption_layer.h8
-rw-r--r--src/core/crypto/encryption_layer.cpp6
-rw-r--r--src/core/crypto/encryption_layer.h8
-rw-r--r--src/core/crypto/key_manager.cpp6
-rw-r--r--src/core/crypto/key_manager.h2
-rw-r--r--src/core/crypto/xts_encryption_layer.cpp4
-rw-r--r--src/core/crypto/xts_encryption_layer.h2
-rw-r--r--src/core/file_sys/bis_factory.cpp12
-rw-r--r--src/core/file_sys/bis_factory.h5
-rw-r--r--src/core/file_sys/card_image.cpp15
-rw-r--r--src/core/file_sys/content_archive.cpp8
-rw-r--r--src/core/file_sys/content_archive.h2
-rw-r--r--src/core/file_sys/control_metadata.cpp26
-rw-r--r--src/core/file_sys/control_metadata.h9
-rw-r--r--src/core/file_sys/directory.h2
-rw-r--r--src/core/file_sys/fsmitm_romfsbuild.cpp366
-rw-r--r--src/core/file_sys/fsmitm_romfsbuild.h70
-rw-r--r--src/core/file_sys/nca_metadata.cpp6
-rw-r--r--src/core/file_sys/nca_metadata.h1
-rw-r--r--src/core/file_sys/nca_patch.cpp34
-rw-r--r--src/core/file_sys/nca_patch.h12
-rw-r--r--src/core/file_sys/partition_filesystem.cpp12
-rw-r--r--src/core/file_sys/partition_filesystem.h4
-rw-r--r--src/core/file_sys/patch_manager.cpp55
-rw-r--r--src/core/file_sys/patch_manager.h3
-rw-r--r--src/core/file_sys/program_metadata.cpp10
-rw-r--r--src/core/file_sys/program_metadata.h9
-rw-r--r--src/core/file_sys/registered_cache.cpp19
-rw-r--r--src/core/file_sys/registered_cache.h2
-rw-r--r--src/core/file_sys/romfs.cpp32
-rw-r--r--src/core/file_sys/romfs.h15
-rw-r--r--src/core/file_sys/romfs_factory.cpp4
-rw-r--r--src/core/file_sys/romfs_factory.h1
-rw-r--r--src/core/file_sys/savedata_factory.cpp15
-rw-r--r--src/core/file_sys/savedata_factory.h1
-rw-r--r--src/core/file_sys/submission_package.h2
-rw-r--r--src/core/file_sys/vfs.cpp67
-rw-r--r--src/core/file_sys/vfs.h47
-rw-r--r--src/core/file_sys/vfs_concat.cpp89
-rw-r--r--src/core/file_sys/vfs_concat.h28
-rw-r--r--src/core/file_sys/vfs_layered.cpp132
-rw-r--r--src/core/file_sys/vfs_layered.h50
-rw-r--r--src/core/file_sys/vfs_offset.cpp26
-rw-r--r--src/core/file_sys/vfs_offset.h27
-rw-r--r--src/core/file_sys/vfs_real.cpp25
-rw-r--r--src/core/file_sys/vfs_real.h9
-rw-r--r--src/core/file_sys/vfs_static.h79
-rw-r--r--src/core/file_sys/vfs_vector.cpp56
-rw-r--r--src/core/file_sys/vfs_vector.h26
-rw-r--r--src/core/file_sys/xts_archive.cpp21
-rw-r--r--src/core/file_sys/xts_archive.h5
-rw-r--r--src/core/gdbstub/gdbstub.cpp81
-rw-r--r--src/core/hle/ipc.h2
-rw-r--r--src/core/hle/ipc_helpers.h19
-rw-r--r--src/core/hle/kernel/address_arbiter.cpp25
-rw-r--r--src/core/hle/kernel/errors.h7
-rw-r--r--src/core/hle/kernel/handle_table.cpp2
-rw-r--r--src/core/hle/kernel/handle_table.h2
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp27
-rw-r--r--src/core/hle/kernel/hle_ipc.h20
-rw-r--r--src/core/hle/kernel/mutex.cpp5
-rw-r--r--src/core/hle/kernel/object.h3
-rw-r--r--src/core/hle/kernel/process.cpp114
-rw-r--r--src/core/hle/kernel/process.h162
-rw-r--r--src/core/hle/kernel/scheduler.cpp16
-rw-r--r--src/core/hle/kernel/scheduler.h4
-rw-r--r--src/core/hle/kernel/shared_memory.cpp16
-rw-r--r--src/core/hle/kernel/shared_memory.h2
-rw-r--r--src/core/hle/kernel/svc.cpp269
-rw-r--r--src/core/hle/kernel/svc_wrap.h78
-rw-r--r--src/core/hle/kernel/thread.cpp69
-rw-r--r--src/core/hle/kernel/thread.h17
-rw-r--r--src/core/hle/kernel/vm_manager.cpp182
-rw-r--r--src/core/hle/kernel/vm_manager.h104
-rw-r--r--src/core/hle/kernel/wait_object.cpp2
-rw-r--r--src/core/hle/kernel/wait_object.h2
-rw-r--r--src/core/hle/service/acc/acc.cpp19
-rw-r--r--src/core/hle/service/acc/profile_manager.cpp26
-rw-r--r--src/core/hle/service/acc/profile_manager.h22
-rw-r--r--src/core/hle/service/am/am.cpp28
-rw-r--r--src/core/hle/service/am/am.h1
-rw-r--r--src/core/hle/service/audio/audout_u.cpp2
-rw-r--r--src/core/hle/service/audio/audren_u.cpp16
-rw-r--r--src/core/hle/service/audio/hwopus.cpp6
-rw-r--r--src/core/hle/service/fatal/fatal.cpp141
-rw-r--r--src/core/hle/service/fatal/fatal.h1
-rw-r--r--src/core/hle/service/fatal/fatal_u.cpp2
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp15
-rw-r--r--src/core/hle/service/filesystem/filesystem.h2
-rw-r--r--src/core/hle/service/hid/hid.cpp42
-rw-r--r--src/core/hle/service/hid/irs.cpp158
-rw-r--r--src/core/hle/service/hid/irs.h27
-rw-r--r--src/core/hle/service/lm/lm.cpp2
-rw-r--r--src/core/hle/service/nfp/nfp.cpp1
-rw-r--r--src/core/hle/service/nifm/nifm.cpp19
-rw-r--r--src/core/hle/service/nim/nim.cpp103
-rw-r--r--src/core/hle/service/ns/pl_u.cpp18
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp2
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp2
-rw-r--r--src/core/hle/service/service.cpp6
-rw-r--r--src/core/hle/service/service.h6
-rw-r--r--src/core/hle/service/set/set.cpp10
-rw-r--r--src/core/hle/service/set/set.h2
-rw-r--r--src/core/hle/service/sm/controller.cpp3
-rw-r--r--src/core/hle/service/sm/sm.cpp13
-rw-r--r--src/core/hle/service/sm/sm.h6
-rw-r--r--src/core/hle/service/spl/module.cpp2
-rw-r--r--src/core/hle/service/ssl/ssl.cpp8
-rw-r--r--src/core/hle/service/vi/vi.cpp47
-rw-r--r--src/core/hle/service/vi/vi.h5
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp21
-rw-r--r--src/core/loader/deconstructed_rom_directory.h3
-rw-r--r--src/core/loader/elf.cpp40
-rw-r--r--src/core/loader/elf.h5
-rw-r--r--src/core/loader/loader.cpp2
-rw-r--r--src/core/loader/loader.h3
-rw-r--r--src/core/loader/nax.cpp28
-rw-r--r--src/core/loader/nax.h6
-rw-r--r--src/core/loader/nca.cpp2
-rw-r--r--src/core/loader/nca.h3
-rw-r--r--src/core/loader/nro.cpp14
-rw-r--r--src/core/loader/nro.h3
-rw-r--r--src/core/loader/nso.cpp31
-rw-r--r--src/core/loader/nso.h4
-rw-r--r--src/core/loader/nsp.cpp4
-rw-r--r--src/core/loader/nsp.h2
-rw-r--r--src/core/loader/xci.cpp4
-rw-r--r--src/core/loader/xci.h2
-rw-r--r--src/core/memory.cpp97
-rw-r--r--src/core/memory.h77
-rw-r--r--src/core/memory_hook.h4
-rw-r--r--src/core/tracer/recorder.cpp2
-rw-r--r--src/tests/CMakeLists.txt2
-rw-r--r--src/tests/common/ring_buffer.cpp20
-rw-r--r--src/tests/core/arm/arm_test_common.cpp18
-rw-r--r--src/tests/core/arm/arm_test_common.h8
-rw-r--r--src/tests/glad.cpp14
-rw-r--r--src/video_core/CMakeLists.txt3
-rw-r--r--src/video_core/command_processor.cpp4
-rw-r--r--src/video_core/engines/fermi_2d.h2
-rw-r--r--src/video_core/engines/kepler_memory.cpp45
-rw-r--r--src/video_core/engines/kepler_memory.h90
-rw-r--r--src/video_core/engines/maxwell_3d.cpp13
-rw-r--r--src/video_core/engines/maxwell_3d.h48
-rw-r--r--src/video_core/engines/maxwell_compute.cpp19
-rw-r--r--src/video_core/engines/maxwell_compute.h36
-rw-r--r--src/video_core/engines/maxwell_dma.cpp2
-rw-r--r--src/video_core/engines/maxwell_dma.h2
-rw-r--r--src/video_core/engines/shader_bytecode.h250
-rw-r--r--src/video_core/engines/shader_header.h103
-rw-r--r--src/video_core/gpu.cpp2
-rw-r--r--src/video_core/gpu.h4
-rw-r--r--src/video_core/macro_interpreter.h2
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp15
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.h16
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp74
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h19
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.cpp59
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer_cache.h71
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp8
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h2
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp270
-rw-r--r--src/video_core/renderer_opengl/gl_shader_gen.h18
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.h3
-rw-r--r--src/video_core/renderer_opengl/gl_state.cpp34
-rw-r--r--src/video_core/renderer_opengl/gl_state.h9
-rw-r--r--src/video_core/renderer_opengl/gl_stream_buffer.cpp4
-rw-r--r--src/video_core/textures/decoders.cpp106
-rw-r--r--src/video_core/utils.h22
-rw-r--r--src/yuzu/configuration/configure_gamelist.cpp2
-rw-r--r--src/yuzu/debugger/graphics/graphics_breakpoints.cpp3
-rw-r--r--src/yuzu/debugger/graphics/graphics_surface.cpp4
-rw-r--r--src/yuzu/debugger/wait_tree.cpp3
-rw-r--r--src/yuzu/game_list.cpp33
-rw-r--r--src/yuzu/game_list.h39
-rw-r--r--src/yuzu/game_list_p.h52
-rw-r--r--src/yuzu/main.cpp151
-rw-r--r--src/yuzu/main.h2
-rw-r--r--src/yuzu/util/util.cpp3
-rw-r--r--src/yuzu_cmd/config.cpp6
-rw-r--r--src/yuzu_cmd/default_ini.h2
-rw-r--r--src/yuzu_cmd/yuzu.cpp4
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 @@
1dist/languages/* linguist-vendored
2dist/qt_themes/* linguist-vendored
3externals/* 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
33deploy: 46deploy:
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
12else 12else
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
14fi 17fi
15 18
16mv "$REV_NAME" $RELEASE_NAME 19mv "$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
2mkdir "$HOME/.ccache" || true
3docker 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
3docker 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
3cd /yuzu
4MINGW_PACKAGES="sdl2-mingw-w64 qt5base-mingw-w64 qt5tools-mingw-w64 libsamplerate-mingw-w64 qt5multimedia-mingw-w64"
5apt-get update
6apt-get install -y gpg wget git python3-pip python ccache g++-mingw-w64-x86-64 gcc-mingw-w64-x86-64 mingw-w64-tools cmake
7echo 'deb http://ppa.launchpad.net/tobydox/mingw-w64/ubuntu bionic main ' > /etc/apt/sources.list.d/extras.list
8apt-key adv --keyserver keyserver.ubuntu.com --recv '72931B477E22FEFD47F8DECE02FE5F12ADDE29B2'
9apt-get update
10apt-get install -y ${MINGW_PACKAGES}
11
12# fix a problem in current MinGW headers
13wget -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
15echo 'max_size = 3.0G' > "$HOME/.ccache/ccache.conf"
16
17# Dirty hack to trick unicorn makefile into believing we are in a MINGW system
18mv /bin/uname /bin/uname1 && echo -e '#!/bin/sh\necho MINGW64' >> /bin/uname
19chmod +x /bin/uname
20
21# Dirty hack to trick unicorn makefile into believing we have cmd
22echo '' >> /bin/cmd
23chmod +x /bin/cmd
24
25mkdir build && cd build
26cmake .. -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_UNICORN=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release
27make -j4
28
29# Clean up the dirty hacks
30rm /bin/uname && mv /bin/uname1 /bin/uname
31rm /bin/cmd
32
33ccache -s
34
35echo "Tests skipped"
36#ctest -VV -C Release
37
38echo 'Prepare binaries...'
39cd ..
40mkdir package
41
42QT_PLATFORM_DLL_PATH='/usr/x86_64-w64-mingw32/lib/qt5/plugins/platforms/'
43find build/ -name "yuzu*.exe" -exec cp {} 'package' \;
44
45# copy Qt plugins
46mkdir package/platforms
47cp "${QT_PLATFORM_DLL_PATH}/qwindows.dll" package/platforms/
48cp -rv "${QT_PLATFORM_DLL_PATH}/../mediaservice/" package/
49cp -rv "${QT_PLATFORM_DLL_PATH}/../imageformats/" package/
50rm -f package/mediaservice/*d.dll
51
52for 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}"
56done
57
58pip3 install pefile
59python3 .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 @@
1import pefile
2import sys
3import re
4import os
5import queue
6import shutil
7
8# constant definitions
9KNOWN_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
13DLL_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
19missing = []
20
21
22def 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
38def 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
69def 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
77def 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
89def 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
105if __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
5REV_NAME="yuzu-windows-mingw-${GITDATE}-${GITREV}"
6ARCHIVE_NAME="${REV_NAME}.tar.gz"
7COMPRESSION_FLAGS="-czvf"
8
9mkdir "$REV_NAME"
10# get around the permission issues
11cp -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
6cd /yuzu 6cd /yuzu
7 7
8mkdir build && cd build 8mkdir build && cd build
9cmake .. -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 9cmake .. -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
10ninja 10ninja
11 11
12ccache -s
13
12ctest -VV -C Release 14ctest -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
3set -o pipefail 3set -o pipefail
4 4
5export MACOSX_DEPLOYMENT_TARGET=10.12 5export MACOSX_DEPLOYMENT_TARGET=10.13
6export Qt5_DIR=$(brew --prefix)/opt/qt5 6export Qt5_DIR=$(brew --prefix)/opt/qt5
7export UNICORNDIR=$(pwd)/externals/unicorn 7export UNICORNDIR=$(pwd)/externals/unicorn
8export PATH="/usr/local/opt/ccache/libexec:$PATH" 8export PATH="/usr/local/opt/ccache/libexec:$PATH"
9 9
10mkdir build && cd build 10mkdir build && cd build
11cmake --version 11cmake --version
12cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON 12cmake .. -DYUZU_USE_BUNDLED_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON
13make -j4 13make -j4
14 14
15ccache -s
16
15ctest -VV -C Release 17ctest -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
287if (UNICORN_FOUND) 293if (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}")
291else() 298else()
@@ -431,8 +438,12 @@ enable_testing()
431add_subdirectory(externals) 438add_subdirectory(externals)
432add_subdirectory(src) 439add_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
435set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT yuzu) 442if(ENABLE_QT)
443 set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT yuzu)
444else()
445 set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT yuzu-cmd)
446endif()
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 @@
1set(MINGW_PREFIX /usr/x86_64-w64-mingw32/)
2set(CMAKE_SYSTEM_NAME Windows)
3set(CMAKE_SYSTEM_PROCESSOR x86_64)
4# Actually a hack, w/o this will cause some strange errors
5set(CMAKE_HOST_WIN32 TRUE)
6
7
8set(CMAKE_FIND_ROOT_PATH ${MINGW_PREFIX})
9set(SDL2_PATH ${MINGW_PREFIX})
10set(MINGW_TOOL_PREFIX ${CMAKE_SYSTEM_PROCESSOR}-w64-mingw32-)
11
12# Specify the cross compiler
13set(CMAKE_C_COMPILER ${MINGW_TOOL_PREFIX}gcc-posix)
14set(CMAKE_CXX_COMPILER ${MINGW_TOOL_PREFIX}g++-posix)
15set(CMAKE_RC_COMPILER ${MINGW_TOOL_PREFIX}windres)
16
17# Mingw tools
18set(STRIP ${MINGW_TOOL_PREFIX}strip)
19set(WINDRES ${MINGW_TOOL_PREFIX}windres)
20set(ENV{PKG_CONFIG} ${MINGW_TOOL_PREFIX}pkg-config)
21
22# ccache wrapper
23option(USE_CCACHE "Use ccache for compilation" OFF)
24if(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)
33endif(USE_CCACHE)
34
35# Search for programs in the build host directories
36set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
37
38
39# Echo modified cmake vars to screen for debugging purposes
40if(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)
54endif()
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
37void Filter::Process(std::vector<s16>& signal) { 37void 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.
57static double CascadingBiquadQ(size_t total_count, size_t index) { 57static 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
62CascadingFilter CascadingFilter::LowPass(double cutoff, size_t cascade_size) { 62CascadingFilter 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
32private: 32private:
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 @@
14namespace AudioCore { 14namespace AudioCore {
15 15
16/// The Lanczos kernel 16/// The Lanczos kernel
17static double Lanczos(size_t a, double x) { 17static 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 @@
12namespace AudioCore { 12namespace AudioCore {
13 13
14struct InterpolationState { 14struct 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
42std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count) { 42std::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
11namespace AudioCore { 14namespace AudioCore {
@@ -13,6 +16,41 @@ namespace AudioCore {
13constexpr u32 STREAM_SAMPLE_RATE{48000}; 16constexpr u32 STREAM_SAMPLE_RATE{48000};
14constexpr u32 STREAM_NUM_CHANNELS{2}; 17constexpr u32 STREAM_NUM_CHANNELS{2};
15 18
19class AudioRenderer::VoiceState {
20public:
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
42private:
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
16AudioRenderer::AudioRenderer(AudioRendererParameter params, 54AudioRenderer::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
68AudioRenderer::~AudioRenderer() = default;
69
30u32 AudioRenderer::GetSampleRate() const { 70u32 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
82Stream::State AudioRenderer::GetStreamState() const {
83 return stream->GetState();
84}
85
42std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) { 86std::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
106void AudioRenderer::VoiceState::SetWaveIndex(size_t index) { 150void 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
111std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(size_t sample_count) { 155std::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
212void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) { 256void 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
17namespace Kernel {
18class Event;
19}
18 20
19namespace AudioCore { 21namespace AudioCore {
20 22
23class AudioOut;
24
21enum class PlayState : u8 { 25enum 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
158class AudioRenderer { 162class AudioRenderer {
159public: 163public:
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
168private: 175private:
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
9namespace AudioCore::Codec { 9namespace AudioCore::Codec {
10 10
11std::vector<s16> DecodeADPCM(const u8* const data, size_t size, const ADPCM_Coeff& coeff, 11std::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 */
41std::vector<s16> DecodeADPCM(const u8* const data, size_t size, const ADPCM_Coeff& coeff, 41std::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
18namespace AudioCore { 19namespace AudioCore {
19 20
20constexpr size_t MaxAudioBufferCount{32}; 21constexpr std::size_t MaxAudioBufferCount{32};
21 22
22u32 Stream::GetNumChannels() const { 23u32 Stream::GetNumChannels() const {
23 switch (format) { 24 switch (format) {
@@ -48,11 +49,16 @@ void Stream::Play() {
48} 49}
49 50
50void Stream::Stop() { 51void Stream::Stop() {
52 state = State::Stopped;
51 ASSERT_MSG(false, "Unimplemented"); 53 ASSERT_MSG(false, "Unimplemented");
52} 54}
53 55
56Stream::State Stream::GetState() const {
57 return state;
58}
59
54s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const { 60s64 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
125std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(size_t max_count) { 131std::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
16namespace CoreTiming {
17struct EventType;
18}
18 19
19namespace AudioCore { 20namespace AudioCore {
20 21
22class 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
72private: 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
84private:
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
29size_t TimeStretcher::Process(const s16* in, size_t num_in, s16* out, size_t num_out) { 29std::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 @@
8namespace Common { 8namespace Common {
9 9
10template <typename T> 10template <typename T>
11constexpr T AlignUp(T value, size_t size) { 11constexpr 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
16template <typename T> 16template <typename T>
17constexpr T AlignDown(T value, size_t size) { 17constexpr 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
130public: 130public:
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
117static uint64 HashLen0to16(const char* s, size_t len) { 117static 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.
144static uint64 HashLen17to32(const char* s, size_t len) { 144static 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.
173static uint64 HashLen33to64(const char* s, size_t len) { 173static 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
194uint64 CityHash64(const char* s, size_t len) { 194uint64 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
232uint64 CityHash64WithSeed(const char* s, size_t len, uint64 seed) { 232uint64 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
236uint64 CityHash64WithSeeds(const char* s, size_t len, uint64 seed0, uint64 seed1) { 236uint64 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.
242static uint128 CityMurmur(const char* s, size_t len, uint128 seed) { 242static 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
272uint128 CityHash128WithSeed(const char* s, size_t len, uint128 seed) { 272uint128 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
334uint128 CityHash128(const char* s, size_t len) { 334uint128 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
68namespace Common { 68namespace 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.
80uint64_t CityHash64(const char* buf, size_t len); 80uint64_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.
84uint64_t CityHash64WithSeed(const char* buf, size_t len, uint64_t seed); 84uint64_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.
88uint64_t CityHash64WithSeeds(const char* buf, size_t len, uint64_t seed0, uint64_t seed1); 88uint64_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.
91uint128 CityHash128(const char* s, size_t len); 91uint128 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.
95uint128 CityHash128WithSeed(const char* s, size_t len, uint128 seed); 95uint128 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.
77static void StripTailDirSlashes(std::string& fname) { 77static 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
759size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename) { 761std::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
763size_t ReadFileToString(bool text_file, const char* filename, std::string& str) { 765std::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) {
829std::string_view GetParentPath(std::string_view path) { 831std::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
870std::string_view GetExtensionFromFilename(std::string_view name) { 872std::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();
143std::string AppDataRoamingDirectory(); 145std::string AppDataRoamingDirectory();
144#endif 146#endif
145 147
146size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename); 148std::size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename);
147size_t ReadFileToString(bool text_file, const char* filename, std::string& str); 149
150std::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.
179template <typename T> 182template <typename T>
180std::vector<T> SliceVector(const std::vector<T>& vector, size_t first, size_t last) { 183std::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 */
20static inline u64 ComputeHash64(const void* data, size_t len) { 20static 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
21std::array<u8, 16> operator""_array16(const char* str, size_t len) { 21std::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
32std::array<u8, 32> operator""_array32(const char* str, size_t len) { 32std::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
15u8 ToHexNibble(char c1); 15u8 ToHexNibble(char c1);
16 16
17template <size_t Size, bool le = false> 17template <std::size_t Size, bool le = false>
18std::array<u8, Size> HexStringToArray(std::string_view str) { 18std::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
30template <size_t Size> 30template <std::size_t Size>
31std::string HexArrayToString(std::array<u8, Size> array, bool upper = true) { 31std::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
38std::array<u8, 0x10> operator"" _array16(const char* str, size_t len); 38std::array<u8, 0x10> operator"" _array16(const char* str, std::size_t len);
39std::array<u8, 0x20> operator"" _array32(const char* str, size_t len); 39std::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)
135void FileBackend::Write(const Entry& entry) { 135void 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
101private: 101private:
102 FileUtil::IOFile file; 102 FileUtil::IOFile file;
103 size_t bytes_written; 103 std::size_t bytes_written;
104}; 104};
105 105
106void AddBackend(std::unique_ptr<Backend> backend); 106void 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
73void Filter::SetClassLevel(Class log_class, Level level) { 73void 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
77void Filter::ParseFilterString(std::string_view filter_view) { 77void Filter::ParseFilterString(std::string_view filter_view) {
@@ -93,7 +93,8 @@ void Filter::ParseFilterString(std::string_view filter_view) {
93} 93}
94 94
95bool Filter::CheckMessage(Class log_class, Level level) const { 95bool 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
99bool Filter::IsDebug() const { 100bool 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
51private: 51private:
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
28void* AllocateExecutableMemory(size_t size, bool low) { 28void* 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
77void* AllocateMemoryPages(size_t size) { 77void* 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
93void* AllocateAlignedMemory(size_t size, size_t alignment) { 93void* 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
112void FreeMemoryPages(void* ptr, size_t size) { 112void 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
133void WriteProtectMemory(void* ptr, size_t size, bool allowExecute) { 133void 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
143void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute) { 143void 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
10void* AllocateExecutableMemory(size_t size, bool low = true); 10void* AllocateExecutableMemory(std::size_t size, bool low = true);
11void* AllocateMemoryPages(size_t size); 11void* AllocateMemoryPages(std::size_t size);
12void FreeMemoryPages(void* ptr, size_t size); 12void FreeMemoryPages(void* ptr, std::size_t size);
13void* AllocateAlignedMemory(size_t size, size_t alignment); 13void* AllocateAlignedMemory(std::size_t size, std::size_t alignment);
14void FreeAlignedMemory(void* ptr); 14void FreeAlignedMemory(void* ptr);
15void WriteProtectMemory(void* ptr, size_t size, bool executable = false); 15void WriteProtectMemory(void* ptr, std::size_t size, bool executable = false);
16void UnWriteProtectMemory(void* ptr, size_t size, bool allowExecute = false); 16void UnWriteProtectMemory(void* ptr, std::size_t size, bool allowExecute = false);
17std::string MemUsage(); 17std::string MemUsage();
18 18
19inline int GetPageSize() { 19inline 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.
18std::string GetLastErrorMsg() { 18std::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
22template <typename T, size_t capacity, size_t granularity = 1> 23template <typename T, std::size_t capacity, std::size_t granularity = 1>
23class RingBuffer { 24class 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
34public: 35public:
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
102private: 103private:
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.
40std::string ArrayToString(const u8* data, size_t size, int line_len, bool spaces) { 40std::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.
62std::string StripSpaces(const std::string& str) { 62std::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
174std::string TabsToSpaces(int tab_size, std::string in) { 174std::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
184std::string ReplaceAll(std::string result, const std::string& src, const std::string& dest) { 184std::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
384std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, size_t max_len) { 384std::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
20std::string ToUpper(std::string str); 20std::string ToUpper(std::string str);
21 21
22std::string ArrayToString(const u8* data, size_t size, int line_len = 20, bool spaces = true); 22std::string ArrayToString(const u8* data, std::size_t size, int line_len = 20, bool spaces = true);
23 23
24std::string StringFromBuffer(const std::vector<u8>& data); 24std::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 */
121std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, size_t max_len); 121std::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
61class Barrier { 61class Barrier {
62public: 62public:
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:
80private: 80private:
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
88void SleepCurrentThread(int ms); 88void SleepCurrentThread(int ms);
89void SwitchCurrentThread(); // On Linux, this is equal to sleep 1ms 89void 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.
94inline void YieldCPU() {
95 std::this_thread::yield();
96}
97
98void SetCurrentThreadName(const char* name); 90void 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
100constexpr size_t ABI_SHADOW_SPACE = 0x20; 100constexpr 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
150constexpr size_t ABI_SHADOW_SPACE = 0; 150constexpr std::size_t ABI_SHADOW_SPACE = 0;
151 151
152#endif 152#endif
153 153
154inline void ABI_CalculateFrameSize(BitSet32 regs, size_t rsp_alignment, size_t needed_frame_size, 154inline 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
176inline size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs, 177inline 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
197inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, BitSet32 regs, 199inline 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) {
34template <typename T> 34template <typename T>
35inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) { 35inline 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
10namespace Kernel {
11enum class VMAPermission : u8;
12}
10 13
11namespace Core { 14namespace Core {
12 15
13/// Generic ARM11 CPU interface 16/// Generic ARMv8 CPU interface
14class ARM_Interface : NonCopyable { 17class ARM_Interface : NonCopyable {
15public: 18public:
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
19namespace Core { 21namespace 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
118std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const { 131std::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
160ARM_Dynarmic::ARM_Dynarmic(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, size_t core_index) 174ARM_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
169ARM_Dynarmic::~ARM_Dynarmic() = default; 184ARM_Dynarmic::~ARM_Dynarmic() = default;
170 185
171void ARM_Dynarmic::MapBackingMemory(u64 address, size_t size, u8* memory, 186void 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
176void ARM_Dynarmic::UnmapMemory(u64 address, size_t size) { 191void 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
196u128 ARM_Dynarmic::GetExtReg(int index) const { 211u128 ARM_Dynarmic::GetVectorReg(int index) const {
197 return jit->GetVector(index); 212 return jit->GetVector(index);
198} 213}
199 214
200void ARM_Dynarmic::SetExtReg(int index, u128 value) { 215void ARM_Dynarmic::SetVectorReg(int index, u128 value) {
201 jit->SetVector(index, value); 216 jit->SetVector(index, value);
202} 217}
203 218
204u32 ARM_Dynarmic::GetVFPReg(int /*index*/) const { 219u32 ARM_Dynarmic::GetPSTATE() const {
205 UNIMPLEMENTED();
206 return {};
207}
208
209void ARM_Dynarmic::SetVFPReg(int /*index*/, u32 /*value*/) {
210 UNIMPLEMENTED();
211}
212
213u32 ARM_Dynarmic::GetCPSR() const {
214 return jit->GetPstate(); 220 return jit->GetPstate();
215} 221}
216 222
217void ARM_Dynarmic::SetCPSR(u32 cpsr) { 223void ARM_Dynarmic::SetPSTATE(u32 pstate) {
218 jit->SetPstate(cpsr); 224 jit->SetPstate(pstate);
219} 225}
220 226
221u64 ARM_Dynarmic::GetTlsAddress() const { 227u64 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
246void ARM_Dynarmic::LoadContext(const ThreadContext& ctx) { 254void 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
255void ARM_Dynarmic::PrepareReschedule() { 265void 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
272DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(size_t core_count) : monitor(core_count) {} 282DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(std::size_t core_count) : monitor(core_count) {}
273DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default; 283DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default;
274 284
275void DynarmicExclusiveMonitor::SetExclusive(size_t core_index, VAddr addr) { 285void 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
284bool DynarmicExclusiveMonitor::ExclusiveWrite8(size_t core_index, VAddr vaddr, u8 value) { 294bool 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
289bool DynarmicExclusiveMonitor::ExclusiveWrite16(size_t core_index, VAddr vaddr, u16 value) { 299bool 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
294bool DynarmicExclusiveMonitor::ExclusiveWrite32(size_t core_index, VAddr vaddr, u32 value) { 304bool 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
299bool DynarmicExclusiveMonitor::ExclusiveWrite64(size_t core_index, VAddr vaddr, u64 value) { 309bool 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
304bool DynarmicExclusiveMonitor::ExclusiveWrite128(size_t core_index, VAddr vaddr, u128 value) { 314bool 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
15namespace Memory {
16struct PageTable;
17}
18
15namespace Core { 19namespace Core {
16 20
17class ARM_Dynarmic_Callbacks; 21class ARM_Dynarmic_Callbacks;
@@ -19,24 +23,22 @@ class DynarmicExclusiveMonitor;
19 23
20class ARM_Dynarmic final : public ARM_Interface { 24class ARM_Dynarmic final : public ARM_Interface {
21public: 25public:
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
68class DynarmicExclusiveMonitor final : public ExclusiveMonitor { 70class DynarmicExclusiveMonitor final : public ExclusiveMonitor {
69public: 71public:
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
82private: 84private:
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 {
12public: 12public:
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
93void ARM_Unicorn::MapBackingMemory(VAddr address, size_t size, u8* memory, 93void 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
98void ARM_Unicorn::UnmapMemory(VAddr address, size_t size) { 98void 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
134u128 ARM_Unicorn::GetExtReg(int /*index*/) const { 134u128 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
140void ARM_Unicorn::SetExtReg(int /*index*/, u128 /*value*/) { 140void ARM_Unicorn::SetVectorReg(int /*index*/, u128 /*value*/) {
141 UNIMPLEMENTED(); 141 UNIMPLEMENTED();
142} 142}
143 143
144u32 ARM_Unicorn::GetVFPReg(int /*index*/) const { 144u32 ARM_Unicorn::GetPSTATE() const {
145 UNIMPLEMENTED();
146 return {};
147}
148
149void ARM_Unicorn::SetVFPReg(int /*index*/, u32 /*value*/) {
150 UNIMPLEMENTED();
151}
152
153u32 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
159void ARM_Unicorn::SetCPSR(u32 cpsr) { 150void 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 {
15public: 15public:
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
351size_t System::CurrentCoreIndex() { 351std::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
359const std::shared_ptr<Kernel::Scheduler>& System::Scheduler(size_t core_index) { 359const 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
372ARM_Interface& System::ArmInterface(size_t core_index) { 372ARM_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
377Cpu& System::CpuCore(size_t core_index) { 377Cpu& 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
51Cpu::Cpu(std::shared_ptr<ExclusiveMonitor> exclusive_monitor, 52Cpu::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
69std::shared_ptr<ExclusiveMonitor> Cpu::MakeExclusiveMonitor(size_t num_cores) { 70Cpu::~Cpu() = default;
71
72std::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
15namespace Kernel { 14namespace Kernel {
16class Scheduler; 15class Scheduler;
@@ -19,6 +18,7 @@ class Scheduler;
19namespace Core { 18namespace Core {
20 19
21class ARM_Interface; 20class ARM_Interface;
21class ExclusiveMonitor;
22 22
23constexpr unsigned NUM_CPU_CORES{4}; 23constexpr unsigned NUM_CPU_CORES{4};
24 24
@@ -42,7 +42,8 @@ private:
42class Cpu { 42class Cpu {
43public: 43public:
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
75private: 76private:
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
11namespace Core::Crypto { 11namespace Core::Crypto {
12namespace { 12namespace {
13std::vector<u8> CalculateNintendoTweak(size_t sector_id) { 13std::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
23static_assert(static_cast<size_t>(Mode::CTR) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_CTR), 23static_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.");
25static_assert(static_cast<size_t>(Mode::ECB) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_ECB), 26static_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.");
27static_assert(static_cast<size_t>(Mode::XTS) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_XTS), 29static_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
36template <typename Key, size_t KeySize> 39template <typename Key, std::size_t KeySize>
37Crypto::AESCipher<Key, KeySize>::AESCipher(Key key, Mode mode) 40Crypto::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
57template <typename Key, size_t KeySize> 60template <typename Key, std::size_t KeySize>
58AESCipher<Key, KeySize>::~AESCipher() { 61AESCipher<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
63template <typename Key, size_t KeySize> 66template <typename Key, std::size_t KeySize>
64void AESCipher<Key, KeySize>::SetIV(std::vector<u8> iv) { 67void 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
70template <typename Key, size_t KeySize> 73template <typename Key, std::size_t KeySize>
71void AESCipher<Key, KeySize>::Transcode(const u8* src, size_t size, u8* dest, Op op) const { 74void 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
113template <typename Key, size_t KeySize> 116template <typename Key, std::size_t KeySize>
114void AESCipher<Key, KeySize>::XTSTranscode(const u8* src, size_t size, u8* dest, size_t sector_id, 117void 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
28template <typename Key, size_t KeySize = sizeof(Key)> 28template <typename Key, std::size_t KeySize = sizeof(Key)>
29class AESCipher { 29class 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
61private: 61private:
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
9namespace Core::Crypto { 9namespace Core::Crypto {
10 10
11CTREncryptionLayer::CTREncryptionLayer(FileSys::VirtualFile base_, Key128 key_, size_t base_offset) 11CTREncryptionLayer::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
15size_t CTREncryptionLayer::Read(u8* data, size_t length, size_t offset) const { 16std::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
46void CTREncryptionLayer::UpdateIV(size_t offset) const { 47void 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.
15class CTREncryptionLayer : public EncryptionLayer { 15class CTREncryptionLayer : public EncryptionLayer {
16public: 16public:
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
23private: 23private:
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
15size_t EncryptionLayer::GetSize() const { 15std::size_t EncryptionLayer::GetSize() const {
16 return base->GetSize(); 16 return base->GetSize();
17} 17}
18 18
19bool EncryptionLayer::Resize(size_t new_size) { 19bool 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
35size_t EncryptionLayer::Write(const u8* data, size_t length, size_t offset) { 35std::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 {
15public: 15public:
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
29protected: 29protected:
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
210template <size_t Size> 210template <std::size_t Size>
211void KeyManager::WriteKeyToFile(bool title_key, std::string_view keyname, 211void 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;
14XTSEncryptionLayer::XTSEncryptionLayer(FileSys::VirtualFile base_, Key256 key_) 14XTSEncryptionLayer::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
17size_t XTSEncryptionLayer::Read(u8* data, size_t length, size_t offset) const { 17std::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 {
15public: 15public:
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
20private: 20private:
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
8namespace FileSys { 9namespace FileSys {
9 10
10BISFactory::BISFactory(VirtualDir nand_root_) 11BISFactory::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
28VirtualDir 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.
18class BISFactory { 18class BISFactory {
19public: 19public:
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
26private: 28private:
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
94VirtualDir XCI::GetPartition(XCIPartition partition) const { 95VirtualDir 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
98std::shared_ptr<NSP> XCI::GetSecurePartitionNSP() const { 99std::shared_ptr<NSP> XCI::GetSecurePartitionNSP() const {
@@ -168,11 +169,11 @@ bool XCI::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
168} 169}
169 170
170Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) { 171Loader::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
466NCA::~NCA() = default;
467
466Loader::ResultStatus NCA::GetStatus() const { 468Loader::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 {
81public: 81public:
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
9namespace FileSys { 9namespace FileSys {
10 10
11const 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
11std::string LanguageEntry::GetApplicationName() const { 19std::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
31NACP::~NACP() = default;
32
23const LanguageEntry& NACP::GetLanguageEntry(Language language) const { 33const 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
37std::string NACP::GetApplicationName(Language language) const { 47std::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
69static constexpr std::array<const char*, 15> LANGUAGE_NAMES = { 69extern 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.
78class NACP { 73class NACP {
79public: 74public:
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 {
25struct Entry { 25struct 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
32namespace FileSys {
33
34constexpr u64 FS_MAX_PATH = 0x301;
35
36constexpr u32 ROMFS_ENTRY_EMPTY = 0xFFFFFFFF;
37constexpr u32 ROMFS_FILEPARTITION_OFS = 0x200;
38
39// Types for building a RomFS.
40struct 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};
52static_assert(sizeof(RomFSHeader) == 0x50, "RomFSHeader has incorrect size.");
53
54struct RomFSDirectoryEntry {
55 u32 parent;
56 u32 sibling;
57 u32 child;
58 u32 file;
59 u32 hash;
60 u32 name_size;
61};
62static_assert(sizeof(RomFSDirectoryEntry) == 0x18, "RomFSDirectoryEntry has incorrect size.");
63
64struct RomFSFileEntry {
65 u32 parent;
66 u32 sibling;
67 u64 offset;
68 u64 size;
69 u32 hash;
70 u32 name_size;
71};
72static_assert(sizeof(RomFSFileEntry) == 0x20, "RomFSFileEntry has incorrect size.");
73
74struct RomFSBuildFileContext;
75
76struct 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
87struct 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
99static 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
109static 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
126void 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
176bool 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
193bool 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
211RomFSBuildContext::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
221RomFSBuildContext::~RomFSBuildContext() = default;
222
223std::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
34namespace FileSys {
35
36struct RomFSBuildDirectoryContext;
37struct RomFSBuildFileContext;
38struct RomFSDirectoryEntry;
39struct RomFSFileEntry;
40
41class RomFSBuildContext {
42public:
43 explicit RomFSBuildContext(VirtualDir base);
44 ~RomFSBuildContext();
45
46 // This finalizes the context.
47 std::map<u64, VirtualFile> Build();
48
49private:
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 @@
11namespace FileSys { 11namespace FileSys {
12 12
13bool operator>=(TitleType lhs, TitleType rhs) { 13bool 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
17bool operator<=(TitleType lhs, TitleType rhs) { 17bool 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
21CNMT::CNMT(VirtualFile file) { 21CNMT::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
54CNMT::~CNMT() = default;
55
54u64 CNMT::GetTitleID() const { 56u64 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
38BKTR::~BKTR() = default; 38BKTR::~BKTR() = default;
39 39
40size_t BKTR::Read(u8* data, size_t length, size_t offset) const { 40std::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
112template <bool Subsection, typename BlockType, typename BucketType> 112template <bool Subsection, typename BlockType, typename BucketType>
113std::pair<size_t, size_t> BKTR::SearchBucketEntry(u64 offset, BlockType block, 113std::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
182size_t BKTR::GetSize() const { 182std::size_t BKTR::GetSize() const {
183 return relocation.size; 183 return relocation.size;
184} 184}
185 185
186bool BKTR::Resize(size_t new_size) { 186bool 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
202size_t BKTR::Write(const u8* data, size_t length, size_t offset) { 202std::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
119private: 119private:
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
75PartitionFilesystem::~PartitionFilesystem() = default;
76
75Loader::ResultStatus PartitionFilesystem::GetStatus() const { 77Loader::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 {
25class PartitionFilesystem : public ReadOnlyVfsDirectory { 25class PartitionFilesystem : public ReadOnlyVfsDirectory {
26public: 26public:
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;
21std::string FormatTitleVersion(u32 version, TitleVersionFormat format) { 22std::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
34constexpr std::array<const char*, 1> PATCH_TYPE_NAMES{ 35constexpr std::array<const char*, 2> PATCH_TYPE_NAMES{
35 "Update", 36 "Update",
37 "LayeredFS",
36}; 38};
37 39
38std::string FormatPatchTypeName(PatchType type) { 40std::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
42PatchManager::PatchManager(u64 title_id) : title_id(title_id) {} 44PatchManager::PatchManager(u64 title_id) : title_id(title_id) {}
43 45
46PatchManager::~PatchManager() = default;
47
44VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { 48VirtualDir 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
71static 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
67VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, 109VirtualFile 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
27enum class PatchType { 27enum class PatchType {
28 Update, 28 Update,
29 LayeredFS,
29}; 30};
30 31
31std::string FormatPatchTypeName(PatchType type); 32std::string FormatPatchTypeName(PatchType type);
@@ -34,6 +35,7 @@ std::string FormatPatchTypeName(PatchType type);
34class PatchManager { 35class PatchManager {
35public: 36public:
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
13namespace FileSys { 13namespace FileSys {
14 14
15ProgramMetadata::ProgramMetadata() = default;
16
17ProgramMetadata::~ProgramMetadata() = default;
18
15Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) { 19Loader::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;
17namespace FileSys { 17namespace FileSys {
18 18
19enum class ProgramAddressSpaceType : u8 { 19enum 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
24enum class ProgramFilePermission : u64 { 26enum class ProgramFilePermission : u64 {
@@ -36,6 +38,9 @@ enum class ProgramFilePermission : u64 {
36 */ 38 */
37class ProgramMetadata { 39class ProgramMetadata {
38public: 40public:
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
20namespace FileSys { 20namespace FileSys {
21
22// The size of blocks to use when vfs raw copying into nand.
23constexpr size_t VFS_RC_LARGE_COPY_BLOCK = 0x400000;
24
21std::string RegisteredCacheEntry::DebugInfo() const { 25std::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
486bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) { 491bool 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
28using NcaID = std::array<u8, 0x10>; 28using NcaID = std::array<u8, 0x10>;
29using RegisteredCacheParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>; 29using RegisteredCacheParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>;
30using VfsCopyFunction = std::function<bool(VirtualFile, VirtualFile)>; 30using VfsCopyFunction = std::function<bool(const VirtualFile&, const VirtualFile&, size_t)>;
31 31
32enum class InstallResult { 32enum 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 {
49static_assert(sizeof(FileEntry) == 0x20, "FileEntry has incorrect size."); 51static_assert(sizeof(FileEntry) == 0x20, "FileEntry has incorrect size.");
50 52
51template <typename Entry> 53template <typename Entry>
52static std::pair<Entry, std::string> GetEntry(const VirtualFile& file, size_t offset) { 54static 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
62void ProcessFile(VirtualFile file, size_t file_offset, size_t data_offset, u32 this_file_offset, 64void 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
77void ProcessDirectory(VirtualFile file, size_t dir_offset, size_t file_offset, size_t data_offset, 79void 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
100VirtualDir ExtractRomFS(VirtualFile file) { 103VirtualDir 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
132VirtualFile 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
13namespace FileSys { 14namespace FileSys {
14 15
16struct RomFSHeader;
17
15struct IVFCLevel { 18struct 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};
30static_assert(sizeof(IVFCHeader) == 0xE0, "IVFCHeader has incorrect size."); 33static_assert(sizeof(IVFCHeader) == 0xE0, "IVFCHeader has incorrect size.");
31 34
35enum 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
34VirtualDir ExtractRomFS(VirtualFile file); 42VirtualDir ExtractRomFS(VirtualFile file,
43 RomFSExtractionType type = RomFSExtractionType::Truncated);
44
45// Converts a VFS filesystem into a RomFS binary
46// Returns nullptr on failure
47VirtualFile 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
31RomFSFactory::~RomFSFactory() = default;
32
31ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() { 33ResultVal<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 {
30class RomFSFactory { 30class RomFSFactory {
31public: 31public:
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
21SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {} 21SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {}
22 22
23SaveDataFactory::~SaveDataFactory() = default;
24
23ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescriptor meta) { 25ResultVal<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
48class SaveDataFactory { 48class SaveDataFactory {
49public: 49public:
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;
24class NSP : public ReadOnlyVfsDirectory { 24class NSP : public ReadOnlyVfsDirectory {
25public: 25public:
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
168VfsDirectory::~VfsDirectory() = default; 168VfsDirectory::~VfsDirectory() = default;
169 169
170boost::optional<u8> VfsFile::ReadByte(size_t offset) const { 170boost::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
179std::vector<u8> VfsFile::ReadBytes(size_t size, size_t offset) const { 179std::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
190bool VfsFile::WriteByte(u8 data, size_t offset) { 190bool 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
194size_t VfsFile::WriteBytes(const std::vector<u8>& data, size_t offset) { 194std::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
289size_t VfsDirectory::GetSize() const { 289std::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
402std::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
402std::string VfsDirectory::GetFullPath() const { 411std::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
437bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, size_t block_size) { 446bool 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
457bool VfsRawCopy(VirtualFile src, VirtualFile dest) { 466bool 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
484bool 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
466VirtualDir GetOrCreateDirectoryRelative(const VirtualDir& rel, std::string_view path) { 503VirtualDir 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
314bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, size_t block_size = 0x200); 319bool 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.
319bool VfsRawCopy(VirtualFile src, VirtualFile dest); 325bool 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.
330bool 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
10namespace FileSys { 12namespace FileSys {
11 13
12VirtualFile ConcatenateFiles(std::vector<VirtualFile> files, std::string name) { 14static 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
21ConcatenatedVfsFile::ConcatenatedVfsFile(std::vector<VirtualFile> files_, std::string name) 26ConcatenatedVfsFile::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
35ConcatenatedVfsFile::ConcatenatedVfsFile(std::map<u64, VirtualFile> files_, std::string name)
36 : files(std::move(files_)), name(std::move(name)) {
37 ASSERT(VerifyConcatenationMapContinuity(files));
38}
39
40ConcatenatedVfsFile::~ConcatenatedVfsFile() = default;
41
42VirtualFile 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
52VirtualFile 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
30std::string ConcatenatedVfsFile::GetName() const { 77std::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
38size_t ConcatenatedVfsFile::GetSize() const { 85std::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
44bool ConcatenatedVfsFile::Resize(size_t new_size) { 91bool 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
62size_t ConcatenatedVfsFile::Read(u8* data, size_t length, size_t offset) const { 109std::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
87size_t ConcatenatedVfsFile::Write(const u8* data, size_t length, size_t offset) { 131std::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
91bool ConcatenatedVfsFile::Rename(std::string_view name) { 135bool 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
12namespace FileSys { 12namespace FileSys {
13 13
14// Wrapper function to allow for more efficient handling of files.size() == 0, 1 cases.
15VirtualFile 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.
19class ConcatenatedVfsFile : public VfsFile { 16class 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
24public: 20public:
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
35private: 41private:
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
9namespace FileSys {
10
11LayeredVfsDirectory::LayeredVfsDirectory(std::vector<VirtualDir> dirs, std::string name)
12 : dirs(std::move(dirs)), name(std::move(name)) {}
13
14LayeredVfsDirectory::~LayeredVfsDirectory() = default;
15
16VirtualDir 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
26std::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
36std::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
48std::shared_ptr<VfsFile> LayeredVfsDirectory::GetFile(std::string_view name) const {
49 return GetFileRelative(name);
50}
51
52std::shared_ptr<VfsDirectory> LayeredVfsDirectory::GetSubdirectory(std::string_view name) const {
53 return GetDirectoryRelative(name);
54}
55
56std::string LayeredVfsDirectory::GetFullPath() const {
57 return dirs[0]->GetFullPath();
58}
59
60std::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
75std::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
92bool LayeredVfsDirectory::IsWritable() const {
93 return false;
94}
95
96bool LayeredVfsDirectory::IsReadable() const {
97 return true;
98}
99
100std::string LayeredVfsDirectory::GetName() const {
101 return name.empty() ? dirs[0]->GetName() : name;
102}
103
104std::shared_ptr<VfsDirectory> LayeredVfsDirectory::GetParentDirectory() const {
105 return dirs[0]->GetParentDirectory();
106}
107
108std::shared_ptr<VfsDirectory> LayeredVfsDirectory::CreateSubdirectory(std::string_view name) {
109 return nullptr;
110}
111
112std::shared_ptr<VfsFile> LayeredVfsDirectory::CreateFile(std::string_view name) {
113 return nullptr;
114}
115
116bool LayeredVfsDirectory::DeleteSubdirectory(std::string_view name) {
117 return false;
118}
119
120bool LayeredVfsDirectory::DeleteFile(std::string_view name) {
121 return false;
122}
123
124bool LayeredVfsDirectory::Rename(std::string_view name_) {
125 name = name_;
126 return true;
127}
128
129bool 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
10namespace 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.
15class LayeredVfsDirectory : public VfsDirectory {
16 LayeredVfsDirectory(std::vector<VirtualDir> dirs, std::string name);
17
18public:
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
42protected:
43 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
44
45private:
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
10namespace FileSys { 10namespace FileSys {
11 11
12OffsetVfsFile::OffsetVfsFile(std::shared_ptr<VfsFile> file_, size_t size_, size_t offset_, 12OffsetVfsFile::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
17OffsetVfsFile::~OffsetVfsFile() = default;
18
17std::string OffsetVfsFile::GetName() const { 19std::string OffsetVfsFile::GetName() const {
18 return name.empty() ? file->GetName() : name; 20 return name.empty() ? file->GetName() : name;
19} 21}
20 22
21size_t OffsetVfsFile::GetSize() const { 23std::size_t OffsetVfsFile::GetSize() const {
22 return size; 24 return size;
23} 25}
24 26
25bool OffsetVfsFile::Resize(size_t new_size) { 27bool 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
50size_t OffsetVfsFile::Read(u8* data, size_t length, size_t r_offset) const { 52std::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
54size_t OffsetVfsFile::Write(const u8* data, size_t length, size_t r_offset) { 56std::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
58boost::optional<u8> OffsetVfsFile::ReadByte(size_t r_offset) const { 60boost::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
65std::vector<u8> OffsetVfsFile::ReadBytes(size_t r_size, size_t r_offset) const { 67std::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
73bool OffsetVfsFile::WriteByte(u8 data, size_t r_offset) { 75bool 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
80size_t OffsetVfsFile::WriteBytes(const std::vector<u8>& data, size_t r_offset) { 82std::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
88size_t OffsetVfsFile::GetOffset() const { 90std::size_t OffsetVfsFile::GetOffset() const {
89 return offset; 91 return offset;
90} 92}
91 93
92size_t OffsetVfsFile::TrimToFit(size_t r_size, size_t r_offset) const { 94std::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.
18class OffsetVfsFile : public VfsFile { 18class OffsetVfsFile : public VfsFile {
19public: 19public:
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
41private: 42private:
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
230size_t RealVfsFile::GetSize() const { 230std::size_t RealVfsFile::GetSize() const {
231 return backing->GetSize(); 231 return backing->GetSize();
232} 232}
233 233
234bool RealVfsFile::Resize(size_t new_size) { 234bool 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
250size_t RealVfsFile::Read(u8* data, size_t length, size_t offset) const { 250std::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
256size_t RealVfsFile::Write(const u8* data, size_t length, size_t offset) { 256std::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
416std::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
416bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { 433bool 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
60private: 60private:
@@ -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
102protected: 103protected:
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
13namespace FileSys {
14
15class StaticVfsFile : public VfsFile {
16public:
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
72private:
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
9namespace FileSys { 10namespace FileSys {
11VectorVfsFile::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
14VectorVfsFile::~VectorVfsFile() = default;
15
16std::string VectorVfsFile::GetName() const {
17 return name;
18}
19
20size_t VectorVfsFile::GetSize() const {
21 return data.size();
22}
23
24bool VectorVfsFile::Resize(size_t new_size) {
25 data.resize(new_size);
26 return true;
27}
28
29std::shared_ptr<VfsDirectory> VectorVfsFile::GetContainingDirectory() const {
30 return parent;
31}
32
33bool VectorVfsFile::IsWritable() const {
34 return true;
35}
36
37bool VectorVfsFile::IsReadable() const {
38 return true;
39}
40
41std::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
47std::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
55bool VectorVfsFile::Rename(std::string_view name_) {
56 name = name_;
57 return true;
58}
59
60void VectorVfsFile::Assign(std::vector<u8> new_data) {
61 data = std::move(new_data);
62}
63
10VectorVfsDirectory::VectorVfsDirectory(std::vector<VirtualFile> files_, 64VectorVfsDirectory::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
70VectorVfsDirectory::~VectorVfsDirectory() = default;
71
16std::vector<std::shared_ptr<VfsFile>> VectorVfsDirectory::GetFiles() const { 72std::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
9namespace FileSys { 9namespace FileSys {
10 10
11// An implementation of VfsFile that is backed by a vector optionally supplied upon construction
12class VectorVfsFile : public VfsFile {
13public:
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
30private:
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.
13class VectorVfsDirectory : public VfsDirectory { 38class 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 {
25constexpr u64 NAX_HEADER_PADDING_SIZE = 0x4000; 25constexpr u64 NAX_HEADER_PADDING_SIZE = 0x4000;
26 26
27template <typename SourceData, typename SourceKey, typename Destination> 27template <typename SourceData, typename SourceKey, typename Destination>
28static bool CalculateHMAC256(Destination* out, const SourceKey* key, size_t key_length, 28static 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
48NAX::NAX(VirtualFile file_) : file(std::move(file_)), header(std::make_unique<NAXHeader>()) { 45NAX::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
67NAX::NAX(VirtualFile file_, std::array<u8, 0x10> nca_id) 64NAX::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
72NAX::~NAX() = default;
73
75Loader::ResultStatus NAX::Parse(std::string_view path) { 74Loader::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
141std::shared_ptr<NCA> NAX::AsNCA() const { 140std::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 {
33public: 33public:
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;
65constexpr u32 LR_REGISTER = 30; 67constexpr u32 LR_REGISTER = 30;
66constexpr u32 SP_REGISTER = 31; 68constexpr u32 SP_REGISTER = 31;
67constexpr u32 PC_REGISTER = 32; 69constexpr u32 PC_REGISTER = 32;
68constexpr u32 CPSR_REGISTER = 33; 70constexpr u32 PSTATE_REGISTER = 33;
69constexpr u32 UC_ARM64_REG_Q0 = 34; 71constexpr u32 UC_ARM64_REG_Q0 = 34;
70constexpr u32 FPSCR_REGISTER = 66; 72constexpr 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
73constexpr u32 TODO_DUMMY_REG_997 = 997; 75constexpr 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 */
295static u32 HexToInt(const u8* src, size_t len) { 297static 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 */
310static u64 HexToLong(const u8* src, size_t len) { 312static 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 */
326static void MemToGdbHex(u8* dest, const u8* src, size_t len) { 328static 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 */
341static void GdbHexToMem(u8* dest, const u8* src, size_t len) { 343static 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.
407static u8 ReadByte() { 409static 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.
419static u8 CalculateChecksum(const u8* buffer, size_t length) { 421static 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 */
520static void SendPacket(const char packet) { 522static 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 @@
12namespace IPC { 12namespace 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.
15constexpr size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32); 15constexpr 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
408template <typename T> 401template <typename T>
409Kernel::SharedPtr<T> RequestParser::GetMoveObject(size_t index) { 402Kernel::SharedPtr<T> RequestParser::GetMoveObject(std::size_t index) {
410 return context->GetMoveObject<T>(index); 403 return context->GetMoveObject<T>(index);
411} 404}
412 405
413template <typename T> 406template <typename T>
414Kernel::SharedPtr<T> RequestParser::GetCopyObject(size_t index) { 407Kernel::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.
37static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address) { 37static 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)
66static void WakeThreads(std::vector<SharedPtr<Thread>>& waiting_threads, s32 num_to_wake) { 67static 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);
56constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle); 59constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle);
57constexpr ResultCode ERR_INVALID_PROCESSOR_ID(ErrorModule::Kernel, ErrCodes::InvalidProcessorId); 60constexpr ResultCode ERR_INVALID_PROCESSOR_ID(ErrorModule::Kernel, ErrCodes::InvalidProcessorId);
61constexpr ResultCode ERR_INVALID_SIZE(ErrorModule::Kernel, ErrCodes::InvalidSize);
62constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorModule::Kernel, ErrCodes::AlreadyRegistered);
58constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState); 63constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState);
59constexpr ResultCode ERR_INVALID_THREAD_PRIORITY(ErrorModule::Kernel, 64constexpr ResultCode ERR_INVALID_THREAD_PRIORITY(ErrorModule::Kernel,
60 ErrCodes::InvalidThreadPriority); 65 ErrCodes::InvalidThreadPriority);
@@ -63,7 +68,7 @@ constexpr ResultCode ERR_INVALID_OBJECT_ADDR(-1);
63constexpr ResultCode ERR_NOT_AUTHORIZED(-1); 68constexpr 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.
65constexpr ResultCode ERR_INVALID_HANDLE_OS(-1); 70constexpr ResultCode ERR_INVALID_HANDLE_OS(-1);
66constexpr ResultCode ERR_NOT_FOUND(-1); 71constexpr ResultCode ERR_NOT_FOUND(ErrorModule::Kernel, ErrCodes::NoSuchEntry);
67constexpr ResultCode RESULT_TIMEOUT(ErrorModule::Kernel, ErrCodes::Timeout); 72constexpr 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.
69constexpr ResultCode ERR_NO_PENDING_SESSIONS(-1); 74constexpr 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
67bool HandleTable::IsValid(Handle handle) const { 67bool 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
294size_t HLERequestContext::WriteBuffer(const void* buffer, size_t size, int buffer_index) const { 294std::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
317size_t HLERequestContext::GetReadBufferSize(int buffer_index) const { 318std::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
323size_t HLERequestContext::GetWriteBufferSize(int buffer_index) const { 324std::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
20namespace Kernel { 21namespace 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
100ResultCode Mutex::Release(VAddr address) { 101ResultCode 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>;
97template <typename T> 96template <typename T>
98inline SharedPtr<T> DynamicObjectCast(SharedPtr<Object> object) { 97inline 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
43void Process::ParseKernelCaps(const u32* kernel_caps, size_t len) { 48void 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
54void 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
142void 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 */
177static 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
195VAddr 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
219void 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
131void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) { 227void 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
147ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) { 243ResultVal<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
183ResultCode Process::HeapFree(VAddr target, u32 size) { 279ResultCode 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
20namespace FileSys {
21class ProgramMetadata;
22}
23
20namespace Kernel { 24namespace Kernel {
21 25
22class KernelCore; 26class KernelCore;
@@ -59,7 +63,7 @@ class ResourceLimit;
59 63
60struct CodeSet final : public Object { 64struct 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
240private:
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
208private:
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
18std::mutex Scheduler::scheduler_mutex; 18std::mutex Scheduler::scheduler_mutex;
19 19
20Scheduler::Scheduler(Core::ARM_Interface* cpu_core) : cpu_core(cpu_core) {} 20Scheduler::Scheduler(Core::ARM_Interface& cpu_core) : cpu_core(cpu_core) {}
21 21
22Scheduler::~Scheduler() { 22Scheduler::~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
20class Scheduler final { 20class Scheduler final {
21public: 21public:
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
122ResultCode SharedMemory::Unmap(Process* target_process, VAddr address) { 124ResultCode 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
128VMAPermission SharedMemory::ConvertPermissions(MemoryPermission permission) { 130VMAPermission 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
37namespace Kernel { 37namespace Kernel {
38namespace {
39constexpr 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.
40static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) { 45static 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
56static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { 68static 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) {
63static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { 84static 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
148static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> thread, 178static 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,
260static ResultCode ArbitrateUnlock(VAddr mutex_addr) { 294static 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
366static ResultCode GetThreadContext(Handle handle, VAddr addr) { 418static 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
443static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) { 523static 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
487static void ExitProcess() { 575static 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
525static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, VAddr stack_top, 591static 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
14namespace Kernel { 14namespace Kernel {
15 15
16#define PARAM(n) Core::CurrentArmInterface().GetReg(n) 16static 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
29template <ResultCode func(u64)> 31template <ResultCode func(u64)>
30void SvcWrap() { 32void SvcWrap() {
31 FuncReturn(func(PARAM(0)).raw); 33 FuncReturn(func(Param(0)).raw);
32} 34}
33 35
34template <ResultCode func(u32)> 36template <ResultCode func(u32)>
35void SvcWrap() { 37void SvcWrap() {
36 FuncReturn(func((u32)PARAM(0)).raw); 38 FuncReturn(func((u32)Param(0)).raw);
37} 39}
38 40
39template <ResultCode func(u32, u32)> 41template <ResultCode func(u32, u32)>
40void SvcWrap() { 42void 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
44template <ResultCode func(u32*, u32)> 46template <ResultCode func(u32*, u32)>
45void SvcWrap() { 47void SvcWrap() {
46 u32 param_1 = 0; 48 u32 param_1 = 0;
47 u32 retval = func(&param_1, (u32)PARAM(1)).raw; 49 u32 retval = func(&param_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() {
52template <ResultCode func(u32*, u64)> 54template <ResultCode func(u32*, u64)>
53void SvcWrap() { 55void SvcWrap() {
54 u32 param_1 = 0; 56 u32 param_1 = 0;
55 u32 retval = func(&param_1, PARAM(1)).raw; 57 u32 retval = func(&param_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
60template <ResultCode func(u64, s32)> 62template <ResultCode func(u64, s32)>
61void SvcWrap() { 63void SvcWrap() {
62 FuncReturn(func(PARAM(0), (s32)PARAM(1)).raw); 64 FuncReturn(func(Param(0), (s32)Param(1)).raw);
65}
66
67template <ResultCode func(u64, u32)>
68void SvcWrap() {
69 FuncReturn(func(Param(0), static_cast<u32>(Param(1))).raw);
63} 70}
64 71
65template <ResultCode func(u64*, u64)> 72template <ResultCode func(u64*, u64)>
66void SvcWrap() { 73void SvcWrap() {
67 u64 param_1 = 0; 74 u64 param_1 = 0;
68 u32 retval = func(&param_1, PARAM(1)).raw; 75 u32 retval = func(&param_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
73template <ResultCode func(u32, u64)> 80template <ResultCode func(u32, u64)>
74void SvcWrap() { 81void 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
78template <ResultCode func(u32, u32, u64)> 85template <ResultCode func(u32, u32, u64)>
79void SvcWrap() { 86void 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
83template <ResultCode func(u32, u32*, u64*)> 90template <ResultCode func(u32, u32*, u64*)>
84void SvcWrap() { 91void 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), &param_1, &param_2); 94 ResultCode retval = func((u32)(Param(2) & 0xFFFFFFFF), &param_1, &param_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() {
93template <ResultCode func(u64, u64, u32, u32)> 100template <ResultCode func(u64, u64, u32, u32)>
94void SvcWrap() { 101void 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
99template <ResultCode func(u32, u64, u32)> 106template <ResultCode func(u32, u64, u32)>
100void SvcWrap() { 107void 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
104template <ResultCode func(u64, u64, u64)> 111template <ResultCode func(u64, u64, u64)>
105void SvcWrap() { 112void 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
109template <ResultCode func(u32, u64, u64, u32)> 116template <ResultCode func(u32, u64, u64, u32)>
110void SvcWrap() { 117void 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
114template <ResultCode func(u32, u64, u64)> 121template <ResultCode func(u32, u64, u64)>
115void SvcWrap() { 122void 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
119template <ResultCode func(u32*, u64, u64, s64)> 126template <ResultCode func(u32*, u64, u64, s64)>
120void SvcWrap() { 127void SvcWrap() {
121 u32 param_1 = 0; 128 u32 param_1 = 0;
122 ResultCode retval = func(&param_1, PARAM(1), (u32)(PARAM(2) & 0xFFFFFFFF), (s64)PARAM(3)); 129 ResultCode retval = func(&param_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
127template <ResultCode func(u64, u64, u32, s64)> 134template <ResultCode func(u64, u64, u32, s64)>
128void SvcWrap() { 135void 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
132template <ResultCode func(u64*, u64, u64, u64)> 139template <ResultCode func(u64*, u64, u64, u64)>
133void SvcWrap() { 140void SvcWrap() {
134 u64 param_1 = 0; 141 u64 param_1 = 0;
135 u32 retval = func(&param_1, PARAM(1), PARAM(2), PARAM(3)).raw; 142 u32 retval = func(&param_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)>
141void SvcWrap() { 148void SvcWrap() {
142 u32 param_1 = 0; 149 u32 param_1 = 0;
143 u32 retval = 150 u32 retval =
144 func(&param_1, PARAM(1), PARAM(2), PARAM(3), (u32)PARAM(4), (s32)(PARAM(5) & 0xFFFFFFFF)) 151 func(&param_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)>
151void SvcWrap() { 158void 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() {
165template <ResultCode func(u32*, u64, u64, u32)> 172template <ResultCode func(u32*, u64, u64, u32)>
166void SvcWrap() { 173void SvcWrap() {
167 u32 param_1 = 0; 174 u32 param_1 = 0;
168 u32 retval = func(&param_1, PARAM(1), PARAM(2), (u32)(PARAM(3) & 0xFFFFFFFF)).raw; 175 u32 retval = func(&param_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)>
174void SvcWrap() { 181void SvcWrap() {
175 u32 param_1 = 0; 182 u32 param_1 = 0;
176 u32 retval = 183 u32 retval =
177 func(&param_1, PARAM(1), (u32)(PARAM(2) & 0xFFFFFFFF), (u32)(PARAM(3) & 0xFFFFFFFF)).raw; 184 func(&param_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() {
182template <ResultCode func(u64, u32, s32, s64)> 189template <ResultCode func(u64, u32, s32, s64)>
183void SvcWrap() { 190void 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
189template <ResultCode func(u64, u32, s32, s32)> 196template <ResultCode func(u64, u32, s32, s32)>
190void SvcWrap() { 197void 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
220template <void func(s64)> 227template <void func(s64)>
221void SvcWrap() { 228void SvcWrap() {
222 func((s64)PARAM(0)); 229 func((s64)Param(0));
223} 230}
224 231
225template <void func(u64, u64 len)> 232template <void func(u64, u64 len)>
226void SvcWrap() { 233void SvcWrap() {
227 func(PARAM(0), PARAM(1)); 234 func(Param(0), Param(1));
228} 235}
229 236
230template <void func(u64, u64, u64)> 237template <void func(u64, u64, u64)>
231void SvcWrap() { 238void 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
74void WaitCurrentThread_Sleep() { 71void 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 */
188static 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
224ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name, VAddr entry_point, 195ResultVal<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
313SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 priority, 259SharedPtr<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
63class Thread final : public WaitObject { 63class Thread final : public WaitObject {
64public: 64public:
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 */
283SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 priority, 294SharedPtr<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
56VMManager::VMManager() { 57VMManager::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
60VMManager::~VMManager() { 63VMManager::~VMManager() {
61 Reset(); 64 Reset(FileSys::ProgramAddressSpaceType::Is39Bit);
62} 65}
63 66
64void VMManager::Reset() { 67void 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
79VMManager::VMAHandle VMManager::FindVMA(VAddr target) const { 82VMManager::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
87ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target, 90ResultVal<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
388void 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
451void VMManager::Clear() {
452 ClearVMAMap();
453 ClearPageTable();
454}
455
456void VMManager::ClearVMAMap() {
457 vma_map.clear();
458}
459
460void 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
385u64 VMManager::GetTotalMemoryUsage() const { 467u64 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
395VAddr VMManager::GetAddressSpaceBaseAddr() const { 477VAddr VMManager::GetAddressSpaceBaseAddress() const {
396 LOG_WARNING(Kernel, "(STUBBED) called"); 478 return address_space_base;
397 return 0x8000000; 479}
480
481VAddr VMManager::GetAddressSpaceEndAddress() const {
482 return address_space_end;
398} 483}
399 484
400u64 VMManager::GetAddressSpaceSize() const { 485u64 VMManager::GetAddressSpaceSize() const {
401 LOG_WARNING(Kernel, "(STUBBED) called"); 486 return address_space_end - address_space_base;
402 return MAX_ADDRESS; 487}
488
489u64 VMManager::GetAddressSpaceWidth() const {
490 return address_space_width;
491}
492
493VAddr VMManager::GetCodeRegionBaseAddress() const {
494 return code_region_base;
495}
496
497VAddr VMManager::GetCodeRegionEndAddress() const {
498 return code_region_end;
499}
500
501u64 VMManager::GetCodeRegionSize() const {
502 return code_region_end - code_region_base;
503}
504
505VAddr VMManager::GetHeapRegionBaseAddress() const {
506 return heap_region_base;
507}
508
509VAddr VMManager::GetHeapRegionEndAddress() const {
510 return heap_region_end;
511}
512
513u64 VMManager::GetHeapRegionSize() const {
514 return heap_region_end - heap_region_base;
515}
516
517VAddr VMManager::GetMapRegionBaseAddress() const {
518 return map_region_base;
519}
520
521VAddr VMManager::GetMapRegionEndAddress() const {
522 return map_region_end;
523}
524
525u64 VMManager::GetMapRegionSize() const {
526 return map_region_end - map_region_base;
527}
528
529VAddr VMManager::GetNewMapRegionBaseAddress() const {
530 return new_map_region_base;
531}
532
533VAddr VMManager::GetNewMapRegionEndAddress() const {
534 return new_map_region_end;
535}
536
537u64 VMManager::GetNewMapRegionSize() const {
538 return new_map_region_end - new_map_region_base;
539}
540
541VAddr VMManager::GetTLSIORegionBaseAddress() const {
542 return tls_io_region_base;
543}
544
545VAddr VMManager::GetTLSIORegionEndAddress() const {
546 return tls_io_region_end;
547}
548
549u64 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
15namespace FileSys {
16enum class ProgramAddressSpaceType : u8;
17}
18
15namespace Kernel { 19namespace Kernel {
16 20
17enum class VMAType : u8 { 21enum 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 {
111class VMManager final { 115class VMManager final {
112public: 116public:
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:
69template <> 69template <>
70inline SharedPtr<WaitObject> DynamicObjectCast<WaitObject>(SharedPtr<Object> object) { 70inline 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() {
25ProfileManager::ProfileManager() { 25ProfileManager::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
36boost::optional<size_t> ProfileManager::AddToProfiles(const ProfileInfo& user) { 36boost::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
45bool ProfileManager::RemoveProfileAtIndex(size_t index) { 45bool 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
93ResultCode ProfileManager::CreateNewUser(UUID uuid, const std::string& username) { 93ResultCode 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.
104boost::optional<size_t> ProfileManager::GetUserIndex(const UUID& uuid) const { 105boost::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
117boost::optional<size_t> ProfileManager::GetUserIndex(const ProfileInfo& user) const { 118boost::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:*
122bool ProfileManager::GetProfileBase(boost::optional<size_t> index, ProfileBase& profile) const { 123bool 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.
146size_t ProfileManager::GetUserCount() const { 148
149std::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
153size_t ProfileManager::GetOpenUserCount() const { 156
157std::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.
209bool ProfileManager::GetProfileBaseAndData(boost::optional<size_t> index, ProfileBase& profile, 213bool 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
14namespace Service::Account { 14namespace Service::Account {
15constexpr size_t MAX_USERS = 8; 15constexpr std::size_t MAX_USERS = 8;
16constexpr size_t MAX_DATA = 128; 16constexpr std::size_t MAX_DATA = 128;
17constexpr u128 INVALID_UUID{{0, 0}}; 17constexpr u128 INVALID_UUID{{0, 0}};
18 18
19struct UUID { 19struct 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
111private: 111private:
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
25namespace Service::AM { 26namespace 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
397void 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
396void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) { 412void 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
99static size_t WorkerBufferSize(u32 channel_count) { 99static 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
16Module::Interface::~Interface() = default; 25Module::Interface::~Interface() = default;
17 26
27struct 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};
45static_assert(sizeof(FatalInfo) == 0x250, "FatalInfo is an invalid size");
46
47enum class FatalType : u32 {
48 ErrorReportAndScreen = 0,
49 ErrorReport = 1,
50 ErrorScreen = 2,
51};
52
53static 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
113static 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
130void 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
18void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) { 140void 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
26void Module::Interface::ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx) { 151void 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
9Fatal_U::Fatal_U(std::shared_ptr<Module> module) : Module::Interface(std::move(module), "fatal:u") { 9Fatal_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
346FileSys::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
346void CreateFactories(const FileSys::VirtualFilesystem& vfs, bool overwrite) { 355void 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();
52std::shared_ptr<FileSys::RegisteredCache> GetUserNANDContents(); 52std::shared_ptr<FileSys::RegisteredCache> GetUserNANDContents();
53std::shared_ptr<FileSys::RegisteredCache> GetSDMCContents(); 53std::shared_ptr<FileSys::RegisteredCache> GetSDMCContents();
54 54
55FileSys::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.
57void CreateFactories(const FileSys::VirtualFilesystem& vfs, bool overwrite = true); 59void 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
584class HidDbg final : public ServiceFramework<HidDbg> { 614class 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
7namespace Service::HID { 12namespace Service::HID {
@@ -9,28 +14,145 @@ namespace Service::HID {
9IRS::IRS() : ServiceFramework{"irs"} { 14IRS::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
46void 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
52void 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
58void 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
65void 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
71void 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
77void 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
83void 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
89void 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
97void 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
103void 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
110void 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
116void 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
122void 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
128void 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
134void 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
140void 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
146void 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
152void 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
36IRS::~IRS() = default; 158IRS::~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
10namespace Kernel {
11class SharedMemory;
12}
13
9namespace Service::HID { 14namespace Service::HID {
10 15
11class IRS final : public ServiceFramework<IRS> { 16class IRS final : public ServiceFramework<IRS> {
12public: 17public:
13 explicit IRS(); 18 explicit IRS();
14 ~IRS() override; 19 ~IRS() override;
20
21private:
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
17class IRS_SYS final : public ServiceFramework<IRS_SYS> { 44class 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
63private: 63private:
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
115private: 121private:
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
108class IEnsureNetworkClockAvailabilityService final
109 : public ServiceFramework<IEnsureNetworkClockAvailabilityService> {
110public:
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
130private:
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
103class NTC final : public ServiceFramework<NTC> { 179class NTC final : public ServiceFramework<NTC> {
104public: 180public:
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
193private:
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
118void InstallInterfaces(SM::ServiceManager& sm) { 215void 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
80static void DecryptSharedFont(const std::vector<u32>& input, std::vector<u8>& output, 80static 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
97static void EncryptSharedFont(const std::vector<u8>& input, std::vector<u8>& output, 97static 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
115struct PL_U::Impl { 115struct 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
318void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) { 318void 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
73u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) { 73u32 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
24namespace Service::NVFlinger { 24namespace Service::NVFlinger {
25 25
26constexpr size_t SCREEN_REFRESH_RATE = 60; 26constexpr std::size_t SCREEN_REFRESH_RATE = 60;
27constexpr u64 frame_ticks = static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE); 27constexpr u64 frame_ticks = static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE);
28 28
29NVFlinger::NVFlinger() { 29NVFlinger::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
132void ServiceFrameworkBase::RegisterHandlersBase(const FunctionInfoBase* functions, size_t n) { 132void 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
35constexpr size_t pre4_0_0_max_entries = 0xF; 35constexpr std::size_t pre4_0_0_max_entries = 0xF;
36constexpr size_t post4_0_0_max_entries = 0x40; 36constexpr std::size_t post4_0_0_max_entries = 0x40;
37 37
38LanguageCode GetLanguageCodeFromIndex(size_t index) { 38LanguageCode GetLanguageCodeFromIndex(std::size_t index) {
39 return available_language_codes.at(index); 39 return available_language_codes.at(index);
40} 40}
41 41
42template <size_t size> 42template <std::size_t size>
43static std::array<LanguageCode, size> MakeLanguageCodeSubset() { 43static 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
49static void PushResponseLanguageCode(Kernel::HLERequestContext& ctx, size_t max_size) { 49static 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};
31LanguageCode GetLanguageCodeFromIndex(size_t idx); 31LanguageCode GetLanguageCodeFromIndex(std::size_t idx);
32 32
33class SET final : public ServiceFramework<SET> { 33class SET final : public ServiceFramework<SET> {
34public: 34public:
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
16namespace Service::SM { 16namespace Service::SM {
17 17
18constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorModule::SM, 4);
19constexpr ResultCode ERR_INVALID_NAME(ErrorModule::SM, 6);
20constexpr ResultCode ERR_SERVICE_NOT_REGISTERED(ErrorModule::SM, 7);
21
18ServiceManager::ServiceManager() = default; 22ServiceManager::ServiceManager() = default;
19ServiceManager::~ServiceManager() = default; 23ServiceManager::~ServiceManager() = default;
20 24
@@ -24,10 +28,10 @@ void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) {
24 28
25static ResultCode ValidateServiceName(const std::string& name) { 29static 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
39constexpr ResultCode ERR_SERVICE_NOT_REGISTERED(-1);
40constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(-1);
41constexpr ResultCode ERR_INVALID_NAME_SIZE(-1);
42constexpr ResultCode ERR_NAME_CONTAINS_NUL(-1);
43constexpr ResultCode ERR_ALREADY_REGISTERED(-1);
44
45class ServiceManager { 39class ServiceManager {
46public: 40public:
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;
21void Module::Interface::GetRandomBytes(Kernel::HLERequestContext& ctx) { 21void 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
105private: 105private:
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
27namespace Service::VI { 30namespace Service::VI {
28 31
@@ -38,7 +41,7 @@ static_assert(sizeof(DisplayInfo) == 0x60, "DisplayInfo has wrong size");
38class Parcel { 41class Parcel {
39public: 42public:
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
163class NativeWindow : public Parcel { 166class 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
10namespace CoreTiming { 9namespace Service::NVFlinger {
11struct EventType; 10class NVFlinger;
12} 11}
13 12
14namespace Service::VI { 13namespace 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
23namespace Loader { 21namespace 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
91ResultStatus AppLoader_DeconstructedRomDirectory::Load( 89ResultStatus 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
13namespace Loader { 12namespace 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
194public: 194public:
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
277SharedPtr<CodeSet> ElfReader::LoadInto(u32 vaddr) { 277SharedPtr<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
390ResultStatus AppLoader_ELF::Load(Kernel::SharedPtr<Kernel::Process>& process) { 390ResultStatus 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
14namespace Loader { 11namespace 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
157std::ostream& operator<<(std::ostream& os, ResultStatus status) { 157std::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
17namespace Kernel { 16namespace Kernel {
18struct AddressMapping; 17struct 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
13namespace Loader { 13namespace Loader {
14namespace {
15FileType 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
15AppLoader_NAX::AppLoader_NAX(FileSys::VirtualFile file) 29AppLoader_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)
19AppLoader_NAX::~AppLoader_NAX() = default; 33AppLoader_NAX::~AppLoader_NAX() = default;
20 34
21FileType AppLoader_NAX::IdentifyType(const FileSys::VirtualFile& file) { 35FileType 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; 40FileType AppLoader_NAX::GetFileType() {
41 return IdentifyTypeImpl(*nax);
30} 42}
31 43
32ResultStatus AppLoader_NAX::Load(Kernel::SharedPtr<Kernel::Process>& process) { 44ResultStatus 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
33ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) { 33ResultStatus 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
12namespace FileSys { 11namespace 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
178ResultStatus AppLoader_NRO::Load(Kernel::SharedPtr<Kernel::Process>& process) { 178ResultStatus 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
33struct NsoHeader { 33struct 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};
41static_assert(sizeof(NsoHeader) == 0x6c, "NsoHeader has incorrect size."); 48static_assert(sizeof(NsoHeader) == 0x6c, "NsoHeader has incorrect size.");
42static_assert(std::is_trivially_copyable_v<NsoHeader>, "NsoHeader isn't trivially copyable."); 49static_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
147ResultStatus AppLoader_NSO::Load(Kernel::SharedPtr<Kernel::Process>& process) { 156ResultStatus 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
65ResultStatus AppLoader_NSP::Load(Kernel::SharedPtr<Kernel::Process>& process) { 63ResultStatus 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
49ResultStatus AppLoader_XCI::Load(Kernel::SharedPtr<Kernel::Process>& process) { 47ResultStatus 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
24namespace Memory { 23namespace Memory {
25 24
@@ -41,6 +40,21 @@ PageTable* GetCurrentPageTable() {
41 return current_page_table; 40 return current_page_table;
42} 41}
43 42
43PageTable::PageTable() = default;
44
45PageTable::PageTable(std::size_t address_space_width_in_bits) {
46 Resize(address_space_width_in_bits);
47}
48
49PageTable::~PageTable() = default;
50
51void 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
44static void MapPages(PageTable& page_table, VAddr base, u64 size, u8* memory, PageType type) { 58static 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
105static u8* GetPointerFromVMA(const Kernel::Process& process, VAddr vaddr) { 119static 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
202bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) { 216bool 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
356u8 Read8(const VAddr addr) { 372u8 Read8(const VAddr addr) {
@@ -370,16 +386,16 @@ u64 Read64(const VAddr addr) {
370} 386}
371 387
372void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer, 388void 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
417void ReadBlock(const VAddr src_addr, void* dest_buffer, const size_t size) { 433void 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
437void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer, 453void 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
480void WriteBlock(const VAddr dest_addr, const void* src_buffer, const size_t size) { 496void 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
484void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const size_t size) { 500void 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
525void CopyBlock(const Kernel::Process& process, VAddr dest_addr, VAddr src_addr, const size_t size) { 541void 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
568void CopyBlock(VAddr dest_addr, VAddr src_addr, size_t size) { 585void 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 */
25constexpr size_t PAGE_BITS = 12; 25constexpr std::size_t PAGE_BITS = 12;
26constexpr u64 PAGE_SIZE = 1 << PAGE_BITS; 26constexpr u64 PAGE_SIZE = 1ULL << PAGE_BITS;
27constexpr u64 PAGE_MASK = PAGE_SIZE - 1; 27constexpr u64 PAGE_MASK = PAGE_SIZE - 1;
28constexpr size_t ADDRESS_SPACE_BITS = 36;
29constexpr size_t PAGE_TABLE_NUM_ENTRIES = 1ULL << (ADDRESS_SPACE_BITS - PAGE_BITS);
30 28
31enum class PageType : u8 { 29enum 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 */
64struct PageTable { 62struct 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
85enum : VAddr { 95enum : 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);
154void Write32(VAddr addr, u32 data); 135void Write32(VAddr addr, u32 data);
155void Write64(VAddr addr, u64 data); 136void Write64(VAddr addr, u64 data);
156 137
157void ReadBlock(const Kernel::Process& process, VAddr src_addr, void* dest_buffer, size_t size); 138void ReadBlock(const Kernel::Process& process, VAddr src_addr, void* dest_buffer, std::size_t size);
158void ReadBlock(VAddr src_addr, void* dest_buffer, size_t size); 139void ReadBlock(VAddr src_addr, void* dest_buffer, std::size_t size);
159void WriteBlock(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer, 140void WriteBlock(const Kernel::Process& process, VAddr dest_addr, const void* src_buffer,
160 size_t size); 141 std::size_t size);
161void WriteBlock(VAddr dest_addr, const void* src_buffer, size_t size); 142void WriteBlock(VAddr dest_addr, const void* src_buffer, std::size_t size);
162void ZeroBlock(const Kernel::Process& process, VAddr dest_addr, size_t size); 143void ZeroBlock(const Kernel::Process& process, VAddr dest_addr, std::size_t size);
163void CopyBlock(VAddr dest_addr, VAddr src_addr, size_t size); 144void CopyBlock(VAddr dest_addr, VAddr src_addr, std::size_t size);
164 145
165u8* GetPointer(VAddr vaddr); 146u8* 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
45using MemoryHookPointer = std::shared_ptr<MemoryHook>; 45using 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
11create_target_directory_groups(tests) 10create_target_directory_groups(tests)
12 11
13target_link_libraries(tests PRIVATE common core) 12target_link_libraries(tests PRIVATE common core)
14target_link_libraries(tests PRIVATE glad) # To support linker work-around
15target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} catch-single-include Threads::Threads) 13target_link_libraries(tests PRIVATE ${PLATFORM_LIBRARIES} catch-single-include Threads::Threads)
16 14
17add_test(NAME tests COMMAND tests) 15add_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]") {
79TEST_CASE("RingBuffer: Threaded Test", "[common]") { 79TEST_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
90bool TestEnvironment::TestMemory::ReadBlock(VAddr src_addr, void* dest_buffer, size_t size) { 93bool 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
129bool TestEnvironment::TestMemory::WriteBlock(VAddr dest_addr, const void* src_buffer, size_t size) { 132bool 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;
19namespace ArmTests { 19namespace ArmTests {
20 20
21struct WriteRecord { 21struct 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.
12TEST_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
9namespace Tegra::Engines {
10
11KeplerMemory::KeplerMemory(MemoryManager& memory_manager) : memory_manager(memory_manager) {}
12KeplerMemory::~KeplerMemory() = default;
13
14void 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
32void 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
14namespace Tegra::Engines {
15
16#define KEPLERMEMORY_REG_INDEX(field_name) \
17 (offsetof(Tegra::Engines::KeplerMemory::Regs, field_name) / sizeof(u32))
18
19class KeplerMemory final {
20public:
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
73private:
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
83ASSERT_REG_POSITION(line_length_in, 0x60);
84ASSERT_REG_POSITION(line_count, 0x61);
85ASSERT_REG_POSITION(dest, 0x62);
86ASSERT_REG_POSITION(exec, 0x6C);
87ASSERT_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
249void Maxwell3D::ProcessCBBind(Regs::ShaderStage stage) { 249void 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 {
316std::vector<Texture::FullTextureInfo> Maxwell3D::GetStageTextures(Regs::ShaderStage stage) const { 316std::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
363Texture::FullTextureInfo Maxwell3D::GetStageTexture(Regs::ShaderStage stage, size_t offset) const { 363Texture::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
930private: 941private:
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
979ASSERT_REG_POSITION(macros, 0x45); 990ASSERT_REG_POSITION(macros, 0x45);
991ASSERT_REG_POSITION(tfb_enabled, 0x1D1);
980ASSERT_REG_POSITION(rt, 0x200); 992ASSERT_REG_POSITION(rt, 0x200);
981ASSERT_REG_POSITION(viewport_transform[0], 0x280); 993ASSERT_REG_POSITION(viewport_transform[0], 0x280);
982ASSERT_REG_POSITION(viewport, 0x300); 994ASSERT_REG_POSITION(viewport, 0x300);
@@ -996,6 +1008,7 @@ ASSERT_REG_POSITION(zeta_height, 0x48b);
996ASSERT_REG_POSITION(depth_test_enable, 0x4B3); 1008ASSERT_REG_POSITION(depth_test_enable, 0x4B3);
997ASSERT_REG_POSITION(independent_blend_enable, 0x4B9); 1009ASSERT_REG_POSITION(independent_blend_enable, 0x4B9);
998ASSERT_REG_POSITION(depth_write_enabled, 0x4BA); 1010ASSERT_REG_POSITION(depth_write_enabled, 0x4BA);
1011ASSERT_REG_POSITION(alpha_test_enabled, 0x4BB);
999ASSERT_REG_POSITION(d3d_cull_mode, 0x4C2); 1012ASSERT_REG_POSITION(d3d_cull_mode, 0x4C2);
1000ASSERT_REG_POSITION(depth_test_func, 0x4C3); 1013ASSERT_REG_POSITION(depth_test_func, 0x4C3);
1001ASSERT_REG_POSITION(blend, 0x4CF); 1014ASSERT_REG_POSITION(blend, 0x4CF);
@@ -1009,6 +1022,7 @@ ASSERT_REG_POSITION(stencil_front_func_mask, 0x4E6);
1009ASSERT_REG_POSITION(stencil_front_mask, 0x4E7); 1022ASSERT_REG_POSITION(stencil_front_mask, 0x4E7);
1010ASSERT_REG_POSITION(screen_y_control, 0x4EB); 1023ASSERT_REG_POSITION(screen_y_control, 0x4EB);
1011ASSERT_REG_POSITION(vb_element_base, 0x50D); 1024ASSERT_REG_POSITION(vb_element_base, 0x50D);
1025ASSERT_REG_POSITION(point_size, 0x546);
1012ASSERT_REG_POSITION(zeta_enable, 0x54E); 1026ASSERT_REG_POSITION(zeta_enable, 0x54E);
1013ASSERT_REG_POSITION(tsc, 0x557); 1027ASSERT_REG_POSITION(tsc, 0x557);
1014ASSERT_REG_POSITION(tic, 0x55D); 1028ASSERT_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
7namespace Tegra { 9namespace Tegra {
8namespace Engines { 10namespace Engines {
9 11
10void MaxwellCompute::WriteReg(u32 method, u32 value) {} 12void 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
9namespace Tegra::Engines { 13namespace Tegra::Engines {
10 14
15#define MAXWELL_COMPUTE_REG_INDEX(field_name) \
16 (offsetof(Tegra::Engines::MaxwellCompute::Regs, field_name) / sizeof(u32))
17
11class MaxwellCompute final { 18class MaxwellCompute final {
12public: 19public:
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
52ASSERT_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
21struct Register { 20struct 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
69enum class AttributeSize : u64 {
70 Word = 0,
71 DoubleWord = 1,
72 TripleWord = 2,
73 QuadWord = 3,
74};
75
70union Attribute { 76union 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
242enum 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
235enum class PredicateResultMode : u64 { 277enum 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
266enum class IpaInterpMode : u64 { Linear = 0, Perspective = 1, Flat = 2, Sc = 3 }; 308enum class TextureMiscMode : u64 {
267enum 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
317enum class IpaInterpMode : u64 {
318 Linear = 0,
319 Perspective = 1,
320 Flat = 2,
321 Sc = 3,
322};
323
324enum class IpaSampleMode : u64 {
325 Default = 0,
326 Centroid = 1,
327 Offset = 2,
328};
268 329
269struct IpaMode { 330struct 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:
939private: 1150private:
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
11namespace Tegra::Shader {
12
13enum 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
21struct 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
101static_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
32GPU::~GPU() = default; 34GPU::~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;
102class Maxwell3D; 103class Maxwell3D;
103class MaxwellCompute; 104class MaxwellCompute;
104class MaxwellDMA; 105class MaxwellDMA;
106class KeplerMemory;
105} // namespace Engines 107} // namespace Engines
106 108
107enum class EngineID { 109enum 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
13namespace OpenGL { 13namespace OpenGL {
14 14
15OGLBufferCache::OGLBufferCache(size_t size) : stream_buffer(GL_ARRAY_BUFFER, size) {} 15OGLBufferCache::OGLBufferCache(std::size_t size) : stream_buffer(GL_ARRAY_BUFFER, size) {}
16 16
17GLintptr OGLBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, size_t size, size_t alignment, 17GLintptr 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
56GLintptr OGLBufferCache::UploadHostMemory(const void* raw_pointer, size_t size, size_t alignment) { 56GLintptr 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
66void OGLBufferCache::Map(size_t max_size) { 67void 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
84void OGLBufferCache::AlignBuffer(size_t alignment) { 85void 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
32class OGLBufferCache final : public RasterizerCache<std::shared_ptr<CachedBufferEntry>> { 32class OGLBufferCache final : public RasterizerCache<std::shared_ptr<CachedBufferEntry>> {
33public: 33public:
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
46protected: 46protected:
47 void AlignBuffer(size_t alignment); 47 void AlignBuffer(std::size_t alignment);
48 48
49private: 49private:
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,
46RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo& info) 46RasterizerOpenGL::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
241size_t RasterizerOpenGL::CalculateVertexArraysSize() const { 241std::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
300void RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, bool using_depth_fb, 300void 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
889void 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
900void 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
909void 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
205static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType component_type) { 206static 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
213static bool IsPixelFormatASTC(PixelFormat format) { 214static 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
258template <bool morton_to_gl, PixelFormat format> 262template <bool morton_to_gl, PixelFormat format>
259void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, size_t gl_buffer_size, 263void 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
281static constexpr std::array<void (*)(u32, u32, u32, u8*, size_t, VAddr), 285static 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
338static constexpr std::array<void (*)(u32, u32, u32, u8*, size_t, VAddr), 343static 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
500static void ConvertS8Z24ToZ24S8(std::vector<u8>& data, u32 width, u32 height) { 509static 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
527static void ConvertG8R8ToR8G8(std::vector<u8>& data, u32 width, u32 height) { 536static 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) {
544static void ConvertFormatAsNeeded_LoadGLBuffer(std::vector<u8>& data, PixelFormat pixel_format, 553static 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
730Surface RasterizerCacheOpenGL::GetColorBufferSurface(size_t index, bool preserve_contents) { 741Surface 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> {
711namespace std { 742namespace std {
712template <> 743template <>
713struct hash<SurfaceReserveKey> { 744struct 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
12namespace OpenGL { 13namespace OpenGL {
13 14
14/// Gets the address for the specified shader stage program 15/// Gets the address for the specified shader stage program
15static VAddr GetShaderAddress(Maxwell::ShaderProgram program) { 16static 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
30static void SetShaderUniformBlockBinding(GLuint shader, const char* name, 31static 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
88GLuint CachedShader::GetProgramResourceIndex(const GLShader::ConstBufferEntry& buffer) { 90GLuint 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;
26using Tegra::Shader::SubOp; 27using Tegra::Shader::SubOp;
27 28
28constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH; 29constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH;
29constexpr u32 PROGRAM_HEADER_SIZE = 0x50; 30constexpr u32 PROGRAM_HEADER_SIZE = sizeof(Tegra::Shader::Header);
30 31
31class DecompileFail : public std::runtime_error { 32class DecompileFail : public std::runtime_error {
32public: 33public:
@@ -189,7 +190,7 @@ public:
189 190
190private: 191private:
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
233private: 234private:
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
239enum 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
690private: 731private:
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:
2625private: 2756private:
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
14namespace OpenGL::GLShader { 14namespace OpenGL::GLShader {
15 15
16constexpr size_t MAX_PROGRAM_CODE_LENGTH{0x1000}; 16constexpr std::size_t MAX_PROGRAM_CODE_LENGTH{0x1000};
17using ProgramCode = std::vector<u64>; 17using ProgramCode = std::vector<u64>;
18 18
19class ConstBufferEntry { 19class 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
76public: 76public:
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
139private: 139private:
@@ -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
12namespace OpenGL::GLShader { 12namespace OpenGL::GLShader {
13 13
14/// Number of OpenGL texture samplers that can be used in the fragment shader
15static constexpr size_t NumTextureSamplers = 32;
16
17using Tegra::Engines::Maxwell3D; 14using 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
83void OpenGLState::Apply() const { 86void 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
12namespace OpenGL { 11namespace OpenGL {
13 12
14using Regs = Tegra::Engines::Maxwell3D::Regs;
15
16namespace TextureUnits { 13namespace TextureUnits {
17 14
18struct TextureUnit { 15struct 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 @@
13namespace Tegra::Texture { 13namespace 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 */
19static u32 GetSwizzleOffset(u32 x, u32 y, u32 image_width, u32 bytes_per_pixel, u32 block_height) { 21template <std::size_t N, std::size_t M, u32 Align>
20 // Round up to the next gob 22struct 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 + 39constexpr auto legacy_swizzle_table = SwizzleTable<8, 64, 1>();
24 (x * bytes_per_pixel / 64) * 512 * block_height + 40constexpr 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; 42static 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) {
33void 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
71static 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
100void 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
49u32 BytesPerPixel(TextureFormat format) { 111u32 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
164static 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
91void ConfigureGameList::InitializeRowComboBoxes() { 91void 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
19WaitTreeItem::WaitTreeItem() = default; 20WaitTreeItem::WaitTreeItem() = default;
20WaitTreeItem::~WaitTreeItem() = default; 21WaitTreeItem::~WaitTreeItem() = default;
@@ -117,7 +118,7 @@ QString WaitTreeCallstack::GetText() const {
117std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeCallstack::GetChildren() const { 118std::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
29GameList::SearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist) : gamelist{gamelist} {} 29GameListSearchField::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
32bool GameList::SearchField::KeyReleaseEater::eventFilter(QObject* obj, QEvent* event) { 32bool 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
91void GameList::SearchField::setFilterResult(int visible, int total) { 91void 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
103void GameList::SearchField::clear() { 95void GameListSearchField::clear() {
104 edit_filter->setText(""); 96 edit_filter->setText("");
105} 97}
106 98
107void GameList::SearchField::setFocus() { 99void 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
113GameList::SearchField::SearchField(GameList* parent) : QWidget{parent} { 105GameListSearchField::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
24class GameListWorker; 24class GameListWorker;
25class GameListSearchField;
25class GMainWindow; 26class GMainWindow;
26 27
27namespace FileSys { 28namespace FileSys {
28class VfsFilesystem; 29class VfsFilesystem;
29} 30}
30 31
31enum class GameListOpenTarget { SaveData }; 32enum class GameListOpenTarget {
33 SaveData,
34 ModData,
35};
32 36
33class GameList : public QWidget { 37class 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
118Q_DECLARE_METATYPE(GameListOpenTarget); 99Q_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 {
106public: 107public:
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
181class GameList;
182class QHBoxLayout;
183class QTreeView;
184class QLabel;
185class QLineEdit;
186class QToolButton;
187
188class GameListSearchField : public QWidget {
189 Q_OBJECT
190
191public:
192 explicit GameListSearchField(GameList* parent = nullptr);
193
194 void setFilterResult(int visible, int total);
195
196 void clear();
197 void setFocus();
198
199private:
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.
16static FileSys::VirtualDir VfsFilesystemCreateDirectoryWrapper(
17 const FileSys::VirtualFilesystem& vfs, const std::string& path, FileSys::Mode mode) {
18 return vfs->CreateDirectory(path, mode);
19}
20
21static 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() {
362void GMainWindow::ConnectWidgetEvents() { 380void 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
759static 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
769static 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
799void 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
868void GMainWindow::OnGameListCopyTID(u64 program_id) {
869 QClipboard* clipboard = QGuiApplication::clipboard();
870 clipboard->setText(QString::fromStdString(fmt::format("{:016X}", program_id)));
871}
872
733void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id, 873void 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)
179username = 179username = 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