summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.ci/scripts/linux/docker.sh3
-rw-r--r--.ci/scripts/merge/apply-patches-by-label-private.py45
-rw-r--r--.ci/scripts/merge/apply-patches-by-label.py16
-rw-r--r--.ci/scripts/windows/upload.ps132
-rw-r--r--.ci/templates/build-mock.yml5
-rw-r--r--.ci/templates/build-msvc.yml21
-rw-r--r--.ci/templates/build-single.yml15
-rw-r--r--.ci/templates/merge-private.yml47
-rw-r--r--.ci/templates/mergebot-private.yml30
-rw-r--r--.ci/templates/mergebot.yml2
-rw-r--r--.ci/templates/release-download.yml13
-rw-r--r--.ci/templates/release-github.yml11
-rw-r--r--.ci/templates/release-universal.yml10
-rw-r--r--.ci/yuzu-mainline-step1.yml8
-rw-r--r--.ci/yuzu-mainline-step2.yml63
-rw-r--r--.ci/yuzu-mainline.yml25
-rw-r--r--.ci/yuzu-patreon-step1.yml8
-rw-r--r--.ci/yuzu-patreon-step2.yml30
-rw-r--r--.ci/yuzu-patreon.yml19
-rw-r--r--.gitmodules6
-rw-r--r--CMakeLists.txt2
-rw-r--r--README.md1
-rw-r--r--externals/CMakeLists.txt6
m---------externals/fmt0
m---------externals/libzip0
m---------externals/zlib0
-rw-r--r--license.txt33
-rw-r--r--src/audio_core/audio_renderer.cpp10
-rw-r--r--src/audio_core/audio_renderer.h25
-rw-r--r--src/common/CMakeLists.txt3
-rw-r--r--src/core/CMakeLists.txt20
-rw-r--r--src/core/core.cpp20
-rw-r--r--src/core/core.h8
-rw-r--r--src/core/crypto/key_manager.cpp2
-rw-r--r--src/core/file_sys/bis_factory.cpp5
-rw-r--r--src/core/file_sys/bis_factory.h2
-rw-r--r--src/core/file_sys/vfs_libzip.cpp79
-rw-r--r--src/core/file_sys/vfs_libzip.h13
-rw-r--r--src/core/hle/service/acc/acc.cpp14
-rw-r--r--src/core/hle/service/am/am.cpp100
-rw-r--r--src/core/hle/service/am/am.h7
-rw-r--r--src/core/hle/service/am/applet_ae.h2
-rw-r--r--src/core/hle/service/am/applet_oe.h2
-rw-r--r--src/core/hle/service/am/applets/applets.cpp4
-rw-r--r--src/core/hle/service/am/applets/applets.h2
-rw-r--r--src/core/hle/service/aoc/aoc_u.cpp19
-rw-r--r--src/core/hle/service/aoc/aoc_u.h5
-rw-r--r--src/core/hle/service/bcat/backend/backend.cpp136
-rw-r--r--src/core/hle/service/bcat/backend/backend.h147
-rw-r--r--src/core/hle/service/bcat/backend/boxcat.cpp503
-rw-r--r--src/core/hle/service/bcat/backend/boxcat.h58
-rw-r--r--src/core/hle/service/bcat/bcat.cpp8
-rw-r--r--src/core/hle/service/bcat/bcat.h3
-rw-r--r--src/core/hle/service/bcat/module.cpp557
-rw-r--r--src/core/hle/service/bcat/module.h24
-rw-r--r--src/core/hle/service/btdrv/btdrv.cpp8
-rw-r--r--src/core/hle/service/btdrv/btdrv.h6
-rw-r--r--src/core/hle/service/btm/btm.cpp14
-rw-r--r--src/core/hle/service/btm/btm.h6
-rw-r--r--src/core/hle/service/fatal/fatal.cpp29
-rw-r--r--src/core/hle/service/fatal/fatal.h9
-rw-r--r--src/core/hle/service/fatal/fatal_p.cpp4
-rw-r--r--src/core/hle/service/fatal/fatal_p.h2
-rw-r--r--src/core/hle/service/fatal/fatal_u.cpp3
-rw-r--r--src/core/hle/service/fatal/fatal_u.h2
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp9
-rw-r--r--src/core/hle/service/filesystem/filesystem.h2
-rw-r--r--src/core/hle/service/friend/friend.cpp31
-rw-r--r--src/core/hle/service/friend/friend.h9
-rw-r--r--src/core/hle/service/friend/interface.cpp4
-rw-r--r--src/core/hle/service/friend/interface.h2
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/controller_base.h8
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.cpp3
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.h3
-rw-r--r--src/core/hle/service/hid/controllers/gesture.cpp3
-rw-r--r--src/core/hle/service/hid/controllers/gesture.h3
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.cpp3
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.h3
-rw-r--r--src/core/hle/service/hid/controllers/mouse.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/mouse.h3
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp28
-rw-r--r--src/core/hle/service/hid/controllers/npad.h8
-rw-r--r--src/core/hle/service/hid/controllers/stubbed.cpp3
-rw-r--r--src/core/hle/service/hid/controllers/stubbed.h3
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.cpp3
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h3
-rw-r--r--src/core/hle/service/hid/controllers/xpad.cpp2
-rw-r--r--src/core/hle/service/hid/controllers/xpad.h3
-rw-r--r--src/core/hle/service/hid/hid.cpp25
-rw-r--r--src/core/hle/service/hid/hid.h10
-rw-r--r--src/core/hle/service/hid/irs.cpp6
-rw-r--r--src/core/hle/service/hid/irs.h3
-rw-r--r--src/core/hle/service/ldr/ldr.cpp11
-rw-r--r--src/core/hle/service/ldr/ldr.h2
-rw-r--r--src/core/hle/service/nfp/nfp.cpp19
-rw-r--r--src/core/hle/service/nfp/nfp.h5
-rw-r--r--src/core/hle/service/nfp/nfp_user.cpp4
-rw-r--r--src/core/hle/service/nfp/nfp_user.h2
-rw-r--r--src/core/hle/service/nifm/nifm.cpp43
-rw-r--r--src/core/hle/service/nifm/nifm.h6
-rw-r--r--src/core/hle/service/nim/nim.cpp13
-rw-r--r--src/core/hle/service/nim/nim.h6
-rw-r--r--src/core/hle/service/ns/ns.cpp5
-rw-r--r--src/core/hle/service/ns/ns.h3
-rw-r--r--src/core/hle/service/ns/pl_u.cpp10
-rw-r--r--src/core/hle/service/ns/pl_u.h3
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdevice.h6
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvdisp_disp0.h5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.h5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp18
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp20
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.h8
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.h5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.cpp5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.h5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.cpp5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.h5
-rw-r--r--src/core/hle/service/nvdrv/interface.cpp75
-rw-r--r--src/core/hle/service/nvdrv/interface.h3
-rw-r--r--src/core/hle/service/nvdrv/nvdata.h6
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.cpp7
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.h5
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp38
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h5
-rw-r--r--src/core/hle/service/prepo/prepo.cpp14
-rw-r--r--src/core/hle/service/prepo/prepo.h6
-rw-r--r--src/core/hle/service/service.cpp28
-rw-r--r--src/core/hle/service/time/interface.cpp4
-rw-r--r--src/core/hle/service/time/interface.h2
-rw-r--r--src/core/hle/service/time/time.cpp25
-rw-r--r--src/core/hle/service/time/time.h4
-rw-r--r--src/core/hle/service/vi/display/vi_display.cpp4
-rw-r--r--src/core/hle/service/vi/display/vi_display.h2
-rw-r--r--src/core/loader/nso.cpp1
-rw-r--r--src/core/settings.cpp2
-rw-r--r--src/core/settings.h4
-rw-r--r--src/video_core/engines/fermi_2d.cpp4
-rw-r--r--src/video_core/engines/maxwell_3d.cpp102
-rw-r--r--src/video_core/engines/maxwell_3d.h29
-rw-r--r--src/video_core/engines/shader_bytecode.h8
-rw-r--r--src/video_core/gpu.cpp1
-rw-r--r--src/video_core/gpu.h1
-rw-r--r--src/video_core/macro_interpreter.cpp2
-rw-r--r--src/video_core/morton.cpp2
-rw-r--r--src/video_core/rasterizer_interface.h9
-rw-r--r--src/video_core/renderer_opengl/gl_device.cpp22
-rw-r--r--src/video_core/renderer_opengl/gl_device.h5
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp181
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h9
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp16
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp184
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.h1
-rw-r--r--src/video_core/renderer_opengl/gl_shader_disk_cache.cpp22
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp1
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.cpp1
-rw-r--r--src/video_core/renderer_vulkan/vk_shader_decompiler.cpp15
-rw-r--r--src/video_core/shader/decode/image.cpp137
-rw-r--r--src/video_core/shader/node.h46
-rw-r--r--src/video_core/shader/shader_ir.cpp12
-rw-r--r--src/video_core/shader/shader_ir.h19
-rw-r--r--src/video_core/surface.cpp3
-rw-r--r--src/video_core/surface.h50
-rw-r--r--src/yuzu/CMakeLists.txt7
-rw-r--r--src/yuzu/configuration/config.cpp21
-rw-r--r--src/yuzu/configuration/config.h2
-rw-r--r--src/yuzu/configuration/configure.ui11
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp5
-rw-r--r--src/yuzu/configuration/configure_general.cpp1
-rw-r--r--src/yuzu/configuration/configure_service.cpp136
-rw-r--r--src/yuzu/configuration/configure_service.h34
-rw-r--r--src/yuzu/configuration/configure_service.ui124
-rw-r--r--src/yuzu/main.cpp43
-rw-r--r--src/yuzu/main.h2
-rw-r--r--src/yuzu/uisettings.cpp2
-rw-r--r--src/yuzu/uisettings.h2
-rw-r--r--src/yuzu_cmd/config.cpp5
-rw-r--r--src/yuzu_cmd/default_ini.h5
186 files changed, 3480 insertions, 798 deletions
diff --git a/.ci/scripts/linux/docker.sh b/.ci/scripts/linux/docker.sh
index f538a4081..296b14e56 100644
--- a/.ci/scripts/linux/docker.sh
+++ b/.ci/scripts/linux/docker.sh
@@ -11,4 +11,5 @@ ninja
11 11
12ccache -s 12ccache -s
13 13
14ctest -VV -C Release 14# Ignore zlib's tests, since they aren't gated behind a CMake option.
15ctest -VV -E "(example|example64)" -C Release
diff --git a/.ci/scripts/merge/apply-patches-by-label-private.py b/.ci/scripts/merge/apply-patches-by-label-private.py
new file mode 100644
index 000000000..fe0acd510
--- /dev/null
+++ b/.ci/scripts/merge/apply-patches-by-label-private.py
@@ -0,0 +1,45 @@
1# Download all pull requests as patches that match a specific label
2# Usage: python download-patches-by-label.py <Label to Match> <Root Path Folder to DL to>
3
4import requests, sys, json, shutil, subprocess, os, traceback
5
6org = os.getenv("PRIVATEMERGEORG", "yuzu-emu")
7repo = os.getenv("PRIVATEMERGEREPO", "yuzu-private")
8tagline = sys.argv[3]
9user = sys.argv[1]
10
11dl_list = {}
12
13TAG_NAME = sys.argv[2]
14
15def check_individual(repo_id, pr_id):
16 url = 'https://%sdev.azure.com/%s/%s/_apis/git/repositories/%s/pullRequests/%s/labels?api-version=5.1-preview.1' % (user, org, repo, repo_id, pr_id)
17 response = requests.get(url)
18 if (response.ok):
19 try:
20 js = response.json()
21 return any(tag.get('name') == TAG_NAME for tag in js['value'])
22 except:
23 return False
24 return False
25
26def merge_pr(pn, ref):
27 print("Matched PR# %s" % pn)
28 print(subprocess.check_output(["git", "fetch", "https://%sdev.azure.com/%s/_git/%s" % (user, org, repo), ref, "-f"]))
29 print(subprocess.check_output(["git", "merge", "--squash", 'origin/' + ref.replace('refs/heads/','')]))
30 print(subprocess.check_output(["git", "commit", "-m\"Merge %s PR %s\"" % (tagline, pn)]))
31
32def main():
33 url = 'https://%sdev.azure.com/%s/%s/_apis/git/pullrequests?api-version=5.1' % (user, org, repo)
34 response = requests.get(url)
35 if (response.ok):
36 js = response.json()
37 tagged_prs = filter(lambda pr: check_individual(pr['repository']['id'], pr['pullRequestId']), js['value'])
38 map(lambda pr: merge_pr(pr['pullRequestId'], pr['sourceRefName']), tagged_prs)
39
40if __name__ == '__main__':
41 try:
42 main()
43 except:
44 traceback.print_exc(file=sys.stdout)
45 sys.exit(-1)
diff --git a/.ci/scripts/merge/apply-patches-by-label.py b/.ci/scripts/merge/apply-patches-by-label.py
index b346001a5..43ed74d7f 100644
--- a/.ci/scripts/merge/apply-patches-by-label.py
+++ b/.ci/scripts/merge/apply-patches-by-label.py
@@ -1,7 +1,9 @@
1# Download all pull requests as patches that match a specific label 1# Download all pull requests as patches that match a specific label
2# Usage: python download-patches-by-label.py <Label to Match> <Root Path Folder to DL to> 2# Usage: python download-patches-by-label.py <Label to Match> <Root Path Folder to DL to>
3 3
4import requests, sys, json, urllib3.request, shutil, subprocess 4import requests, sys, json, urllib3.request, shutil, subprocess, os
5
6tagline = sys.argv[2]
5 7
6http = urllib3.PoolManager() 8http = urllib3.PoolManager()
7dl_list = {} 9dl_list = {}
@@ -12,17 +14,23 @@ def check_individual(labels):
12 return True 14 return True
13 return False 15 return False
14 16
15try: 17def do_page(page):
16 url = 'https://api.github.com/repos/yuzu-emu/yuzu/pulls' 18 url = 'https://api.github.com/repos/yuzu-emu/yuzu/pulls?page=%s' % page
17 response = requests.get(url) 19 response = requests.get(url)
18 if (response.ok): 20 if (response.ok):
19 j = json.loads(response.content) 21 j = json.loads(response.content)
22 if j == []:
23 return
20 for pr in j: 24 for pr in j:
21 if (check_individual(pr["labels"])): 25 if (check_individual(pr["labels"])):
22 pn = pr["number"] 26 pn = pr["number"]
23 print("Matched PR# %s" % pn) 27 print("Matched PR# %s" % pn)
24 print(subprocess.check_output(["git", "fetch", "https://github.com/yuzu-emu/yuzu.git", "pull/%s/head:pr-%s" % (pn, pn), "-f"])) 28 print(subprocess.check_output(["git", "fetch", "https://github.com/yuzu-emu/yuzu.git", "pull/%s/head:pr-%s" % (pn, pn), "-f"]))
25 print(subprocess.check_output(["git", "merge", "--squash", "pr-%s" % pn])) 29 print(subprocess.check_output(["git", "merge", "--squash", "pr-%s" % pn]))
26 print(subprocess.check_output(["git", "commit", "-m\"Merge PR %s\"" % pn])) 30 print(subprocess.check_output(["git", "commit", "-m\"Merge %s PR %s\"" % (tagline, pn)]))
31
32try:
33 for i in range(1,30):
34 do_page(i)
27except: 35except:
28 sys.exit(-1) 36 sys.exit(-1)
diff --git a/.ci/scripts/windows/upload.ps1 b/.ci/scripts/windows/upload.ps1
new file mode 100644
index 000000000..de86e7390
--- /dev/null
+++ b/.ci/scripts/windows/upload.ps1
@@ -0,0 +1,32 @@
1$GITDATE = $(git show -s --date=short --format='%ad') -replace "-",""
2$GITREV = $(git show -s --format='%h')
3$RELEASE_DIST = "yuzu-windows-msvc"
4
5$MSVC_BUILD_ZIP = "yuzu-windows-msvc-$GITDATE-$GITREV.zip" -replace " ", ""
6$MSVC_BUILD_PDB = "yuzu-windows-msvc-$GITDATE-$GITREV-debugsymbols.zip" -replace " ", ""
7$MSVC_SEVENZIP = "yuzu-windows-msvc-$GITDATE-$GITREV.7z" -replace " ", ""
8
9$env:BUILD_ZIP = $MSVC_BUILD_ZIP
10$env:BUILD_SYMBOLS = $MSVC_BUILD_PDB
11$env:BUILD_UPDATE = $MSVC_SEVENZIP
12
13$BUILD_DIR = ".\build\bin\Release"
14
15mkdir pdb
16Get-ChildItem "$BUILD_DIR\" -Recurse -Filter "*.pdb" | Copy-Item -destination .\pdb
177z a -tzip $MSVC_BUILD_PDB .\pdb\*.pdb
18rm "$BUILD_DIR\*.pdb"
19mkdir $RELEASE_DIST
20mkdir "artifacts"
21
22Copy-Item "$BUILD_DIR\*" -Destination $RELEASE_DIST -Recurse
23rm "$RELEASE_DIST\*.exe"
24Get-ChildItem "$BUILD_DIR" -Recurse -Filter "yuzu*.exe" | Copy-Item -destination $RELEASE_DIST
25Get-ChildItem "$BUILD_DIR" -Recurse -Filter "QtWebEngineProcess*.exe" | Copy-Item -destination $RELEASE_DIST
26Copy-Item .\license.txt -Destination $RELEASE_DIST
27Copy-Item .\README.md -Destination $RELEASE_DIST
287z a -tzip $MSVC_BUILD_ZIP $RELEASE_DIST\*
297z a $MSVC_SEVENZIP $RELEASE_DIST
30
31Get-ChildItem . -Filter "*.zip" | Copy-Item -destination "artifacts"
32Get-ChildItem . -Filter "*.7z" | Copy-Item -destination "artifacts" \ No newline at end of file
diff --git a/.ci/templates/build-mock.yml b/.ci/templates/build-mock.yml
new file mode 100644
index 000000000..e7aba93de
--- /dev/null
+++ b/.ci/templates/build-mock.yml
@@ -0,0 +1,5 @@
1steps:
2 - script: mkdir artifacts || echo 'X' > artifacts/T1.txt
3 - publish: artifacts
4 artifact: 'yuzu-$(BuildName)-$(BuildSuffix)'
5 displayName: 'Upload Artifacts' \ No newline at end of file
diff --git a/.ci/templates/build-msvc.yml b/.ci/templates/build-msvc.yml
new file mode 100644
index 000000000..906afa382
--- /dev/null
+++ b/.ci/templates/build-msvc.yml
@@ -0,0 +1,21 @@
1parameters:
2 artifactSource: 'true'
3 cache: 'false'
4
5steps:
6- script: mkdir build && cd build && set DATE=`date '+%Y.%m.%d'` && set CI=true && set AZURE_REPO_NAME=yuzu-emu/yuzu-$(BuildName) && set AZURE_REPO_TAG=$(BuildName)-$DATE && cmake -G "Visual Studio 15 2017 Win64" --config Release -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_BUNDLED_UNICORN=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON .. && cd ..
7 displayName: 'Configure CMake'
8- task: MSBuild@1
9 displayName: 'Build'
10 inputs:
11 solution: 'build/yuzu.sln'
12 maximumCpuCount: true
13 configuration: release
14- task: PowerShell@2
15 displayName: 'Package Artifacts'
16 inputs:
17 targetType: 'filePath'
18 filePath: './.ci/scripts/windows/upload.ps1'
19- publish: artifacts
20 artifact: 'yuzu-$(BuildName)-windows-msvc'
21 displayName: 'Upload Artifacts'
diff --git a/.ci/templates/build-single.yml b/.ci/templates/build-single.yml
index 357731eb9..002f5d24f 100644
--- a/.ci/templates/build-single.yml
+++ b/.ci/templates/build-single.yml
@@ -7,14 +7,13 @@ steps:
7 displayName: 'Prepare Environment' 7 displayName: 'Prepare Environment'
8 inputs: 8 inputs:
9 dockerVersion: '17.09.0-ce' 9 dockerVersion: '17.09.0-ce'
10- ${{ if eq(parameters.cache, 'true') }}: 10- task: CacheBeta@0
11 - task: CacheBeta@0 11 displayName: 'Cache Build System'
12 displayName: 'Cache Build System' 12 inputs:
13 inputs: 13 key: yuzu-v1-$(BuildName)-$(BuildSuffix)-$(CacheSuffix)
14 key: yuzu-v1-$(BuildName)-$(BuildSuffix)-$(CacheSuffix) 14 path: $(System.DefaultWorkingDirectory)/ccache
15 path: $(System.DefaultWorkingDirectory)/ccache 15 cacheHitVar: CACHE_RESTORED
16 cacheHitVar: CACHE_RESTORED 16- script: export DATE=`date '+%Y.%m.%d'` && export CI=true && export AZURE_REPO_NAME=yuzu-emu/yuzu-$(BuildName) && export AZURE_REPO_TAG=$(BuildName)-$DATE && chmod a+x ./.ci/scripts/$(ScriptFolder)/exec.sh && ./.ci/scripts/$(ScriptFolder)/exec.sh
17- script: chmod a+x ./.ci/scripts/$(ScriptFolder)/exec.sh && ./.ci/scripts/$(ScriptFolder)/exec.sh
18 displayName: 'Build' 17 displayName: 'Build'
19- script: chmod a+x ./.ci/scripts/$(ScriptFolder)/upload.sh && RELEASE_NAME=$(BuildName) ./.ci/scripts/$(ScriptFolder)/upload.sh 18- script: chmod a+x ./.ci/scripts/$(ScriptFolder)/upload.sh && RELEASE_NAME=$(BuildName) ./.ci/scripts/$(ScriptFolder)/upload.sh
20 displayName: 'Package Artifacts' 19 displayName: 'Package Artifacts'
diff --git a/.ci/templates/merge-private.yml b/.ci/templates/merge-private.yml
new file mode 100644
index 000000000..a640cfbde
--- /dev/null
+++ b/.ci/templates/merge-private.yml
@@ -0,0 +1,47 @@
1jobs:
2- job: merge
3 displayName: 'pull requests'
4 steps:
5 - checkout: self
6 submodules: recursive
7 - template: ./mergebot-private.yml
8 parameters:
9 matchLabel: '$(BuildName)-merge'
10 matchLabelPublic: '$(PublicBuildName)-merge'
11 - task: ArchiveFiles@2
12 displayName: 'Package Source'
13 inputs:
14 rootFolderOrFile: '$(System.DefaultWorkingDirectory)'
15 includeRootFolder: false
16 archiveType: '7z'
17 archiveFile: '$(Build.ArtifactStagingDirectory)/yuzu-$(BuildName)-source.7z'
18 - task: PublishPipelineArtifact@1
19 displayName: 'Upload Artifacts'
20 inputs:
21 targetPath: '$(Build.ArtifactStagingDirectory)/yuzu-$(BuildName)-source.7z'
22 artifact: 'yuzu-$(BuildName)-source'
23 replaceExistingArchive: true
24- job: upload_source
25 displayName: 'upload'
26 dependsOn: merge
27 steps:
28 - template: ./sync-source.yml
29 parameters:
30 artifactSource: 'true'
31 needSubmodules: 'true'
32 - script: chmod a+x $(System.DefaultWorkingDirectory)/.ci/scripts/merge/yuzubot-git-config.sh && $(System.DefaultWorkingDirectory)/.ci/scripts/merge/yuzubot-git-config.sh
33 displayName: 'Apply Git Configuration'
34 - script: git tag -a $(BuildName)-$(Build.BuildId) -m "yuzu $(BuildName) $(Build.BuildNumber) $(Build.DefinitionName)"
35 displayName: 'Tag Source'
36 - script: git remote add other $(GitRepoPushChangesURL)
37 displayName: 'Register Repository'
38 - script: git push --follow-tags --force other HEAD:$(GitPushBranch)
39 displayName: 'Update Code'
40 - script: git rev-list -n 1 $(BuildName)-$(Build.BuildId) > $(Build.ArtifactStagingDirectory)/tag-commit.sha
41 displayName: 'Calculate Release Point'
42 - task: PublishPipelineArtifact@1
43 displayName: 'Upload Release Point'
44 inputs:
45 targetPath: '$(Build.ArtifactStagingDirectory)/tag-commit.sha'
46 artifact: 'yuzu-$(BuildName)-release-point'
47 replaceExistingArchive: true \ No newline at end of file
diff --git a/.ci/templates/mergebot-private.yml b/.ci/templates/mergebot-private.yml
new file mode 100644
index 000000000..f9a40cf61
--- /dev/null
+++ b/.ci/templates/mergebot-private.yml
@@ -0,0 +1,30 @@
1parameters:
2 matchLabel: 'dummy-merge'
3 matchLabelPublic: 'dummy-merge'
4
5steps:
6 - script: mkdir $(System.DefaultWorkingDirectory)/patches && pip install requests urllib3
7 displayName: 'Prepare Environment'
8 - script: chmod a+x $(System.DefaultWorkingDirectory)/.ci/scripts/merge/yuzubot-git-config.sh && $(System.DefaultWorkingDirectory)/.ci/scripts/merge/yuzubot-git-config.sh
9 displayName: 'Apply Git Configuration'
10 - task: PythonScript@0
11 displayName: 'Discover, Download, and Apply Patches (Mainline)'
12 inputs:
13 scriptSource: 'filePath'
14 scriptPath: '.ci/scripts/merge/apply-patches-by-label.py'
15 arguments: '${{ parameters.matchLabelPublic }} $(MergeTaglinePublic) patches-public'
16 workingDirectory: '$(System.DefaultWorkingDirectory)'
17 - task: PythonScript@0
18 displayName: 'Discover, Download, and Apply Patches (Patreon Public)'
19 inputs:
20 scriptSource: 'filePath'
21 scriptPath: '.ci/scripts/merge/apply-patches-by-label.py'
22 arguments: '${{ parameters.matchLabel }} "$(MergeTaglinePrivate) Public" patches-mixed-public'
23 workingDirectory: '$(System.DefaultWorkingDirectory)'
24 - task: PythonScript@0
25 displayName: 'Discover, Download, and Apply Patches (Patreon Private)'
26 inputs:
27 scriptSource: 'filePath'
28 scriptPath: '.ci/scripts/merge/apply-patches-by-label-private.py'
29 arguments: '$(PrivateMergeUser) ${{ parameters.matchLabel }} "$(MergeTaglinePrivate) Private" patches-private'
30 workingDirectory: '$(System.DefaultWorkingDirectory)'
diff --git a/.ci/templates/mergebot.yml b/.ci/templates/mergebot.yml
index 5211efcc6..a4c5c2a28 100644
--- a/.ci/templates/mergebot.yml
+++ b/.ci/templates/mergebot.yml
@@ -11,5 +11,5 @@ steps:
11 inputs: 11 inputs:
12 scriptSource: 'filePath' 12 scriptSource: 'filePath'
13 scriptPath: '.ci/scripts/merge/apply-patches-by-label.py' 13 scriptPath: '.ci/scripts/merge/apply-patches-by-label.py'
14 arguments: '${{ parameters.matchLabel }} patches' 14 arguments: '${{ parameters.matchLabel }} Tagged patches'
15 workingDirectory: '$(System.DefaultWorkingDirectory)' 15 workingDirectory: '$(System.DefaultWorkingDirectory)'
diff --git a/.ci/templates/release-download.yml b/.ci/templates/release-download.yml
new file mode 100644
index 000000000..f7e30690f
--- /dev/null
+++ b/.ci/templates/release-download.yml
@@ -0,0 +1,13 @@
1steps:
2 - task: DownloadPipelineArtifact@2
3 displayName: 'Download Windows Release'
4 inputs:
5 artifactName: 'yuzu-$(BuildName)-windows-msvc'
6 buildType: 'current'
7 targetPath: '$(Build.ArtifactStagingDirectory)'
8 - task: DownloadPipelineArtifact@2
9 displayName: 'Download Linux Release'
10 inputs:
11 artifactName: 'yuzu-$(BuildName)-linux'
12 buildType: 'current'
13 targetPath: '$(Build.ArtifactStagingDirectory)' \ No newline at end of file
diff --git a/.ci/templates/release-github.yml b/.ci/templates/release-github.yml
new file mode 100644
index 000000000..39fd47f1c
--- /dev/null
+++ b/.ci/templates/release-github.yml
@@ -0,0 +1,11 @@
1steps:
2 - template: ./release-download.yml
3 - task: GitHubRelease@0
4 inputs:
5 action: 'create'
6 title: 'yuzu $(BuildName) #$(Build.BuildId)'
7 assets: '$(Build.ArtifactStagingDirectory)/*'
8 gitHubConnection: $(GitHubReleaseConnectionName)
9 repositoryName: '$(Build.Repository.Name)'
10 target: '$(Build.SourceVersion)'
11 tagSource: 'auto' \ No newline at end of file
diff --git a/.ci/templates/release-universal.yml b/.ci/templates/release-universal.yml
new file mode 100644
index 000000000..707697007
--- /dev/null
+++ b/.ci/templates/release-universal.yml
@@ -0,0 +1,10 @@
1steps:
2 - template: ./release-download.yml
3 - task: UniversalPackages@0
4 displayName: Publish Artifacts
5 inputs:
6 command: publish
7 publishDirectory: '$(Build.ArtifactStagingDirectory)'
8 vstsFeedPublish: 'yuzu-$(BuildName)'
9 vstsFeedPackagePublish: 'main'
10 packagePublishDescription: 'Yuzu Windows and Linux Executable Packages' \ No newline at end of file
diff --git a/.ci/yuzu-mainline-step1.yml b/.ci/yuzu-mainline-step1.yml
new file mode 100644
index 000000000..3fd33d75a
--- /dev/null
+++ b/.ci/yuzu-mainline-step1.yml
@@ -0,0 +1,8 @@
1trigger:
2- master
3
4stages:
5- stage: merge
6 displayName: 'merge'
7 jobs:
8 - template: ./templates/merge.yml
diff --git a/.ci/yuzu-mainline-step2.yml b/.ci/yuzu-mainline-step2.yml
new file mode 100644
index 000000000..0c1af397b
--- /dev/null
+++ b/.ci/yuzu-mainline-step2.yml
@@ -0,0 +1,63 @@
1trigger:
2- master
3
4stages:
5- stage: format
6 displayName: 'format'
7 jobs:
8 - job: format
9 displayName: 'clang'
10 pool:
11 vmImage: ubuntu-latest
12 steps:
13 - template: ./templates/format-check.yml
14- stage: build
15 dependsOn: format
16 displayName: 'build'
17 jobs:
18 - job: build
19 displayName: 'standard'
20 pool:
21 vmImage: ubuntu-latest
22 strategy:
23 maxParallel: 10
24 matrix:
25 linux:
26 BuildSuffix: 'linux'
27 ScriptFolder: 'linux'
28 steps:
29 - template: ./templates/sync-source.yml
30 parameters:
31 artifactSource: $(parameters.artifactSource)
32 needSubmodules: 'true'
33 - template: ./templates/build-single.yml
34 parameters:
35 artifactSource: 'false'
36 cache: 'true'
37- stage: build_win
38 dependsOn: format
39 displayName: 'build-windows'
40 jobs:
41 - job: build
42 displayName: 'msvc'
43 pool:
44 vmImage: vs2017-win2016
45 steps:
46 - template: ./templates/sync-source.yml
47 parameters:
48 artifactSource: $(parameters.artifactSource)
49 needSubmodules: 'true'
50 - template: ./templates/build-msvc.yml
51 parameters:
52 artifactSource: 'false'
53 cache: 'true'
54- stage: release
55 displayName: 'Release'
56 dependsOn:
57 - build
58 - build_win
59 jobs:
60 - job: github
61 displayName: 'GitHub Release'
62 steps:
63 - template: ./templates/release-github.yml \ No newline at end of file
diff --git a/.ci/yuzu-mainline.yml b/.ci/yuzu-mainline.yml
deleted file mode 100644
index 2930a8564..000000000
--- a/.ci/yuzu-mainline.yml
+++ /dev/null
@@ -1,25 +0,0 @@
1trigger:
2- master
3
4stages:
5- stage: merge
6 displayName: 'merge'
7 jobs:
8 - template: ./templates/merge.yml
9- stage: format
10 dependsOn: merge
11 displayName: 'format'
12 jobs:
13 - job: format
14 displayName: 'clang'
15 pool:
16 vmImage: ubuntu-latest
17 steps:
18 - template: ./templates/format-check.yml
19- stage: build
20 displayName: 'build'
21 dependsOn: format
22 jobs:
23 - template: ./templates/build-standard.yml
24 parameters:
25 cache: 'true'
diff --git a/.ci/yuzu-patreon-step1.yml b/.ci/yuzu-patreon-step1.yml
new file mode 100644
index 000000000..cf30397cd
--- /dev/null
+++ b/.ci/yuzu-patreon-step1.yml
@@ -0,0 +1,8 @@
1trigger:
2- master
3
4stages:
5- stage: merge
6 displayName: 'merge'
7 jobs:
8 - template: ./templates/merge-private.yml
diff --git a/.ci/yuzu-patreon-step2.yml b/.ci/yuzu-patreon-step2.yml
new file mode 100644
index 000000000..23e45d9dd
--- /dev/null
+++ b/.ci/yuzu-patreon-step2.yml
@@ -0,0 +1,30 @@
1trigger:
2- master
3
4stages:
5- stage: format
6 displayName: 'format'
7 jobs:
8 - job: format
9 displayName: 'clang'
10 pool:
11 vmImage: ubuntu-latest
12 steps:
13 - template: ./templates/format-check.yml
14- stage: build
15 dependsOn: format
16 displayName: 'build'
17 jobs:
18 - job: build
19 displayName: 'windows-msvc'
20 pool:
21 vmImage: vs2017-win2016
22 steps:
23 - template: ./templates/sync-source.yml
24 parameters:
25 artifactSource: $(parameters.artifactSource)
26 needSubmodules: 'true'
27 - template: ./templates/build-msvc.yml
28 parameters:
29 artifactSource: 'false'
30 cache: $(parameters.cache)
diff --git a/.ci/yuzu-patreon.yml b/.ci/yuzu-patreon.yml
deleted file mode 100644
index aa912913d..000000000
--- a/.ci/yuzu-patreon.yml
+++ /dev/null
@@ -1,19 +0,0 @@
1# Starter pipeline
2# Start with a minimal pipeline that you can customize to build and deploy your code.
3# Add steps that build, run tests, deploy, and more:
4# https://aka.ms/yaml
5
6trigger:
7- master
8
9pool:
10 vmImage: 'ubuntu-latest'
11
12steps:
13- script: echo Hello, world!
14 displayName: 'Run a one-line script'
15
16- script: |
17 echo Add other tasks to build, test, and deploy your project.
18 echo See https://aka.ms/yaml
19 displayName: 'Run a multi-line script'
diff --git a/.gitmodules b/.gitmodules
index 3a49c4874..35e0d1240 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -46,3 +46,9 @@
46[submodule "sirit"] 46[submodule "sirit"]
47 path = externals/sirit 47 path = externals/sirit
48 url = https://github.com/ReinUsesLisp/sirit 48 url = https://github.com/ReinUsesLisp/sirit
49[submodule "libzip"]
50 path = externals/libzip
51 url = https://github.com/DarkLordZach/libzip
52[submodule "zlib"]
53 path = externals/zlib
54 url = https://github.com/madler/zlib
diff --git a/CMakeLists.txt b/CMakeLists.txt
index bfa104034..9b3b0d6d5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -21,6 +21,8 @@ option(YUZU_USE_BUNDLED_UNICORN "Build/Download bundled Unicorn" ON)
21 21
22option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OFF) 22option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OFF)
23 23
24option(YUZU_ENABLE_BOXCAT "Enable the Boxcat service, a yuzu high-level implementation of BCAT" ON)
25
24option(ENABLE_CUBEB "Enables the cubeb audio backend" ON) 26option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
25 27
26option(ENABLE_VULKAN "Enables Vulkan backend" ON) 28option(ENABLE_VULKAN "Enables Vulkan backend" ON)
diff --git a/README.md b/README.md
index ecace43f2..bdee2e872 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,6 @@
1yuzu emulator 1yuzu emulator
2============= 2=============
3[![Travis CI Build Status](https://travis-ci.org/yuzu-emu/yuzu.svg?branch=master)](https://travis-ci.org/yuzu-emu/yuzu) 3[![Travis CI Build Status](https://travis-ci.org/yuzu-emu/yuzu.svg?branch=master)](https://travis-ci.org/yuzu-emu/yuzu)
4[![AppVeyor CI Build Status](https://ci.appveyor.com/api/projects/status/77k97svb2usreu68?svg=true)](https://ci.appveyor.com/project/bunnei/yuzu)
5[![Azure Mainline CI Build Status](https://dev.azure.com/yuzu-emu/yuzu/_apis/build/status/yuzu%20mainline?branchName=master)](https://dev.azure.com/yuzu-emu/yuzu/) 4[![Azure Mainline CI Build Status](https://dev.azure.com/yuzu-emu/yuzu/_apis/build/status/yuzu%20mainline?branchName=master)](https://dev.azure.com/yuzu-emu/yuzu/)
6 5
7yuzu is an experimental open-source emulator for the Nintendo Switch from the creators of [Citra](https://citra-emu.org/). 6yuzu is an experimental open-source emulator for the Nintendo Switch from the creators of [Citra](https://citra-emu.org/).
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt
index e6fa11a03..3539828b8 100644
--- a/externals/CMakeLists.txt
+++ b/externals/CMakeLists.txt
@@ -77,6 +77,12 @@ if (ENABLE_VULKAN)
77 add_subdirectory(sirit) 77 add_subdirectory(sirit)
78endif() 78endif()
79 79
80# zlib
81add_subdirectory(zlib EXCLUDE_FROM_ALL)
82
83# libzip
84add_subdirectory(libzip EXCLUDE_FROM_ALL)
85
80if (ENABLE_WEB_SERVICE) 86if (ENABLE_WEB_SERVICE)
81 # LibreSSL 87 # LibreSSL
82 set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "") 88 set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")
diff --git a/externals/fmt b/externals/fmt
Subproject 7512a55aa3ae309587ca89668ef9ec4074a51a1 Subproject 4b8f8fac96a7819f28f4be523ca10a2d5d8aaaf
diff --git a/externals/libzip b/externals/libzip
new file mode 160000
Subproject bd7a8103e96bc6d50164447f6b7b57bb786d8e2
diff --git a/externals/zlib b/externals/zlib
new file mode 160000
Subproject cacf7f1d4e3d44d871b605da3b647f07d718623
diff --git a/license.txt b/license.txt
index 2b858f9a7..bf5aec0e6 100644
--- a/license.txt
+++ b/license.txt
@@ -341,15 +341,24 @@ Public License instead of this License.
341 341
342The icons used in this project have the following licenses: 342The icons used in this project have the following licenses:
343 343
344Icon Name | License | Origin/Author 344Icon Name | License | Origin/Author
345--- | --- | --- 345--- | --- | ---
346checked.png | Free for non-commercial use 346checked.png | Free for non-commercial use
347failed.png | Free for non-commercial use 347failed.png | Free for non-commercial use
348lock.png | CC BY-ND 3.0 | https://icons8.com 348lock.png | CC BY-ND 3.0 | https://icons8.com
349plus_folder.png | CC BY-ND 3.0 | https://icons8.com 349plus_folder.png (Default, Dark) | CC BY-ND 3.0 | https://icons8.com
350bad_folder.png | CC BY-ND 3.0 | https://icons8.com 350bad_folder.png (Default, Dark) | CC BY-ND 3.0 | https://icons8.com
351chip.png | CC BY-ND 3.0 | https://icons8.com 351chip.png (Default, Dark) | CC BY-ND 3.0 | https://icons8.com
352folder.png | CC BY-ND 3.0 | https://icons8.com 352folder.png (Default, Dark) | CC BY-ND 3.0 | https://icons8.com
353plus.png (Default, Dark) | CC0 1.0 | Designed by BreadFish64 from the Citra team 353plus.png (Default, Dark) | CC0 1.0 | Designed by BreadFish64 from the Citra team
354plus.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com 354sd_card.png (Default, Dark) | CC BY-ND 3.0 | https://icons8.com
355sd_card.png | CC BY-ND 3.0 | https://icons8.com 355plus_folder.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
356bad_folder.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
357chip.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
358folder.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
359plus.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
360sd_card.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
361
362Note:
363Some icons are different in different themes, and they are separately listed
364only when they have different licenses/origins.
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp
index da50a0bbc..e6f38d600 100644
--- a/src/audio_core/audio_renderer.cpp
+++ b/src/audio_core/audio_renderer.cpp
@@ -107,6 +107,11 @@ Stream::State AudioRenderer::GetStreamState() const {
107 return stream->GetState(); 107 return stream->GetState();
108} 108}
109 109
110static constexpr u32 VersionFromRevision(u32_le rev) {
111 // "REV7" -> 7
112 return ((rev >> 24) & 0xff) - 0x30;
113}
114
110std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) { 115std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) {
111 // Copy UpdateDataHeader struct 116 // Copy UpdateDataHeader struct
112 UpdateDataHeader config{}; 117 UpdateDataHeader config{};
@@ -166,6 +171,11 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_
166 // Copy output header 171 // Copy output header
167 UpdateDataHeader response_data{worker_params}; 172 UpdateDataHeader response_data{worker_params};
168 std::vector<u8> output_params(response_data.total_size); 173 std::vector<u8> output_params(response_data.total_size);
174 const auto audren_revision = VersionFromRevision(config.revision);
175 if (audren_revision >= 5) {
176 response_data.frame_count = 0x10;
177 response_data.total_size += 0x10;
178 }
169 std::memcpy(output_params.data(), &response_data, sizeof(UpdateDataHeader)); 179 std::memcpy(output_params.data(), &response_data, sizeof(UpdateDataHeader));
170 180
171 // Copy output memory pool entries 181 // Copy output memory pool entries
diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h
index 45afbe759..4f14b91cd 100644
--- a/src/audio_core/audio_renderer.h
+++ b/src/audio_core/audio_renderer.h
@@ -194,21 +194,24 @@ struct UpdateDataHeader {
194 mixes_size = 0x0; 194 mixes_size = 0x0;
195 sinks_size = config.sink_count * 0x20; 195 sinks_size = config.sink_count * 0x20;
196 performance_manager_size = 0x10; 196 performance_manager_size = 0x10;
197 frame_count = 0;
197 total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size + voices_size + 198 total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size + voices_size +
198 effects_size + sinks_size + performance_manager_size; 199 effects_size + sinks_size + performance_manager_size;
199 } 200 }
200 201
201 u32_le revision; 202 u32_le revision{};
202 u32_le behavior_size; 203 u32_le behavior_size{};
203 u32_le memory_pools_size; 204 u32_le memory_pools_size{};
204 u32_le voices_size; 205 u32_le voices_size{};
205 u32_le voice_resource_size; 206 u32_le voice_resource_size{};
206 u32_le effects_size; 207 u32_le effects_size{};
207 u32_le mixes_size; 208 u32_le mixes_size{};
208 u32_le sinks_size; 209 u32_le sinks_size{};
209 u32_le performance_manager_size; 210 u32_le performance_manager_size{};
210 INSERT_PADDING_WORDS(6); 211 INSERT_PADDING_WORDS(1);
211 u32_le total_size; 212 u32_le frame_count{};
213 INSERT_PADDING_WORDS(4);
214 u32_le total_size{};
212}; 215};
213static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size"); 216static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size");
214 217
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 01abdb3bb..dfed8b51d 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -10,6 +10,9 @@ if (DEFINED ENV{CI})
10 elseif(DEFINED ENV{APPVEYOR}) 10 elseif(DEFINED ENV{APPVEYOR})
11 set(BUILD_REPOSITORY $ENV{APPVEYOR_REPO_NAME}) 11 set(BUILD_REPOSITORY $ENV{APPVEYOR_REPO_NAME})
12 set(BUILD_TAG $ENV{APPVEYOR_REPO_TAG_NAME}) 12 set(BUILD_TAG $ENV{APPVEYOR_REPO_TAG_NAME})
13 elseif(DEFINED ENV{AZURE})
14 set(BUILD_REPOSITORY $ENV{AZURE_REPO_NAME})
15 set(BUILD_TAG $ENV{AZURE_REPO_TAG})
13 endif() 16 endif()
14endif() 17endif()
15add_custom_command(OUTPUT scm_rev.cpp 18add_custom_command(OUTPUT scm_rev.cpp
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index a6b56c9c6..3416854db 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -1,3 +1,9 @@
1if (YUZU_ENABLE_BOXCAT)
2 set(BCAT_BOXCAT_ADDITIONAL_SOURCES hle/service/bcat/backend/boxcat.cpp hle/service/bcat/backend/boxcat.h)
3else()
4 set(BCAT_BOXCAT_ADDITIONAL_SOURCES)
5endif()
6
1add_library(core STATIC 7add_library(core STATIC
2 arm/arm_interface.h 8 arm/arm_interface.h
3 arm/arm_interface.cpp 9 arm/arm_interface.cpp
@@ -82,6 +88,8 @@ add_library(core STATIC
82 file_sys/vfs_concat.h 88 file_sys/vfs_concat.h
83 file_sys/vfs_layered.cpp 89 file_sys/vfs_layered.cpp
84 file_sys/vfs_layered.h 90 file_sys/vfs_layered.h
91 file_sys/vfs_libzip.cpp
92 file_sys/vfs_libzip.h
85 file_sys/vfs_offset.cpp 93 file_sys/vfs_offset.cpp
86 file_sys/vfs_offset.h 94 file_sys/vfs_offset.h
87 file_sys/vfs_real.cpp 95 file_sys/vfs_real.cpp
@@ -241,6 +249,9 @@ add_library(core STATIC
241 hle/service/audio/errors.h 249 hle/service/audio/errors.h
242 hle/service/audio/hwopus.cpp 250 hle/service/audio/hwopus.cpp
243 hle/service/audio/hwopus.h 251 hle/service/audio/hwopus.h
252 hle/service/bcat/backend/backend.cpp
253 hle/service/bcat/backend/backend.h
254 ${BCAT_BOXCAT_ADDITIONAL_SOURCES}
244 hle/service/bcat/bcat.cpp 255 hle/service/bcat/bcat.cpp
245 hle/service/bcat/bcat.h 256 hle/service/bcat/bcat.h
246 hle/service/bcat/module.cpp 257 hle/service/bcat/module.cpp
@@ -499,6 +510,15 @@ create_target_directory_groups(core)
499 510
500target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) 511target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
501target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt json-headers mbedtls opus unicorn open_source_archives) 512target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt json-headers mbedtls opus unicorn open_source_archives)
513
514if (YUZU_ENABLE_BOXCAT)
515 get_directory_property(OPENSSL_LIBS
516 DIRECTORY ${PROJECT_SOURCE_DIR}/externals/libressl
517 DEFINITION OPENSSL_LIBS)
518 target_compile_definitions(core PRIVATE -DCPPHTTPLIB_OPENSSL_SUPPORT -DYUZU_ENABLE_BOXCAT)
519 target_link_libraries(core PRIVATE httplib json-headers ${OPENSSL_LIBS} zip)
520endif()
521
502if (ENABLE_WEB_SERVICE) 522if (ENABLE_WEB_SERVICE)
503 target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE) 523 target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE)
504 target_link_libraries(core PRIVATE web_service) 524 target_link_libraries(core PRIVATE web_service)
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 76bb2bae9..75a7ffb97 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -163,6 +163,7 @@ struct System::Impl {
163 gpu_core = VideoCore::CreateGPU(system); 163 gpu_core = VideoCore::CreateGPU(system);
164 164
165 is_powered_on = true; 165 is_powered_on = true;
166 exit_lock = false;
166 167
167 LOG_DEBUG(Core, "Initialized OK"); 168 LOG_DEBUG(Core, "Initialized OK");
168 169
@@ -249,6 +250,7 @@ struct System::Impl {
249 perf_stats->GetMeanFrametime()); 250 perf_stats->GetMeanFrametime());
250 251
251 is_powered_on = false; 252 is_powered_on = false;
253 exit_lock = false;
252 254
253 // Shutdown emulation session 255 // Shutdown emulation session
254 renderer.reset(); 256 renderer.reset();
@@ -333,9 +335,11 @@ struct System::Impl {
333 std::unique_ptr<Core::Hardware::InterruptManager> interrupt_manager; 335 std::unique_ptr<Core::Hardware::InterruptManager> interrupt_manager;
334 CpuCoreManager cpu_core_manager; 336 CpuCoreManager cpu_core_manager;
335 bool is_powered_on = false; 337 bool is_powered_on = false;
338 bool exit_lock = false;
336 339
337 std::unique_ptr<Memory::CheatEngine> cheat_engine; 340 std::unique_ptr<Memory::CheatEngine> cheat_engine;
338 std::unique_ptr<Tools::Freezer> memory_freezer; 341 std::unique_ptr<Tools::Freezer> memory_freezer;
342 std::array<u8, 0x20> build_id{};
339 343
340 /// Frontend applets 344 /// Frontend applets
341 Service::AM::Applets::AppletManager applet_manager; 345 Service::AM::Applets::AppletManager applet_manager;
@@ -629,6 +633,22 @@ const Service::APM::Controller& System::GetAPMController() const {
629 return impl->apm_controller; 633 return impl->apm_controller;
630} 634}
631 635
636void System::SetExitLock(bool locked) {
637 impl->exit_lock = locked;
638}
639
640bool System::GetExitLock() const {
641 return impl->exit_lock;
642}
643
644void System::SetCurrentProcessBuildID(std::array<u8, 32> id) {
645 impl->build_id = id;
646}
647
648const std::array<u8, 32>& System::GetCurrentProcessBuildID() const {
649 return impl->build_id;
650}
651
632System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) { 652System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
633 return impl->Init(*this, emu_window); 653 return impl->Init(*this, emu_window);
634} 654}
diff --git a/src/core/core.h b/src/core/core.h
index d2a3c82d8..f49b7fbf9 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -326,6 +326,14 @@ public:
326 326
327 const Service::APM::Controller& GetAPMController() const; 327 const Service::APM::Controller& GetAPMController() const;
328 328
329 void SetExitLock(bool locked);
330
331 bool GetExitLock() const;
332
333 void SetCurrentProcessBuildID(std::array<u8, 0x20> id);
334
335 const std::array<u8, 0x20>& GetCurrentProcessBuildID() const;
336
329private: 337private:
330 System(); 338 System();
331 339
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index 46aceec3d..222fc95ba 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -423,7 +423,7 @@ static std::optional<u64> FindTicketOffset(const std::array<u8, size>& data) {
423std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket, 423std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket,
424 const RSAKeyPair<2048>& key) { 424 const RSAKeyPair<2048>& key) {
425 const auto issuer = ticket.GetData().issuer; 425 const auto issuer = ticket.GetData().issuer;
426 if (issuer == std::array<u8, 0x40>{}) 426 if (IsAllZeroArray(issuer))
427 return {}; 427 return {};
428 if (issuer[0] != 'R' || issuer[1] != 'o' || issuer[2] != 'o' || issuer[3] != 't') { 428 if (issuer[0] != 'R' || issuer[1] != 'o' || issuer[2] != 'o' || issuer[3] != 't') {
429 LOG_INFO(Crypto, "Attempting to parse ticket with non-standard certificate authority."); 429 LOG_INFO(Crypto, "Attempting to parse ticket with non-standard certificate authority.");
diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp
index 8f758d6d9..0af44f340 100644
--- a/src/core/file_sys/bis_factory.cpp
+++ b/src/core/file_sys/bis_factory.cpp
@@ -136,4 +136,9 @@ u64 BISFactory::GetFullNANDTotalSpace() const {
136 return static_cast<u64>(Settings::values.nand_total_size); 136 return static_cast<u64>(Settings::values.nand_total_size);
137} 137}
138 138
139VirtualDir BISFactory::GetBCATDirectory(u64 title_id) const {
140 return GetOrCreateDirectoryRelative(nand_root,
141 fmt::format("/system/save/bcat/{:016X}", title_id));
142}
143
139} // namespace FileSys 144} // namespace FileSys
diff --git a/src/core/file_sys/bis_factory.h b/src/core/file_sys/bis_factory.h
index bdfe728c9..8f0451c98 100644
--- a/src/core/file_sys/bis_factory.h
+++ b/src/core/file_sys/bis_factory.h
@@ -61,6 +61,8 @@ public:
61 u64 GetUserNANDTotalSpace() const; 61 u64 GetUserNANDTotalSpace() const;
62 u64 GetFullNANDTotalSpace() const; 62 u64 GetFullNANDTotalSpace() const;
63 63
64 VirtualDir GetBCATDirectory(u64 title_id) const;
65
64private: 66private:
65 VirtualDir nand_root; 67 VirtualDir nand_root;
66 VirtualDir load_root; 68 VirtualDir load_root;
diff --git a/src/core/file_sys/vfs_libzip.cpp b/src/core/file_sys/vfs_libzip.cpp
new file mode 100644
index 000000000..8bdaa7e4a
--- /dev/null
+++ b/src/core/file_sys/vfs_libzip.cpp
@@ -0,0 +1,79 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <string>
6#include <zip.h>
7#include "common/logging/backend.h"
8#include "core/file_sys/vfs.h"
9#include "core/file_sys/vfs_libzip.h"
10#include "core/file_sys/vfs_vector.h"
11
12namespace FileSys {
13
14VirtualDir ExtractZIP(VirtualFile file) {
15 zip_error_t error{};
16
17 const auto data = file->ReadAllBytes();
18 std::unique_ptr<zip_source_t, decltype(&zip_source_close)> src{
19 zip_source_buffer_create(data.data(), data.size(), 0, &error), zip_source_close};
20 if (src == nullptr)
21 return nullptr;
22
23 std::unique_ptr<zip_t, decltype(&zip_close)> zip{zip_open_from_source(src.get(), 0, &error),
24 zip_close};
25 if (zip == nullptr)
26 return nullptr;
27
28 std::shared_ptr<VectorVfsDirectory> out = std::make_shared<VectorVfsDirectory>();
29
30 const auto num_entries = zip_get_num_entries(zip.get(), 0);
31
32 zip_stat_t stat{};
33 zip_stat_init(&stat);
34
35 for (std::size_t i = 0; i < num_entries; ++i) {
36 const auto stat_res = zip_stat_index(zip.get(), i, 0, &stat);
37 if (stat_res == -1)
38 return nullptr;
39
40 const std::string name(stat.name);
41 if (name.empty())
42 continue;
43
44 if (name.back() != '/') {
45 std::unique_ptr<zip_file_t, decltype(&zip_fclose)> file{
46 zip_fopen_index(zip.get(), i, 0), zip_fclose};
47
48 std::vector<u8> buf(stat.size);
49 if (zip_fread(file.get(), buf.data(), buf.size()) != buf.size())
50 return nullptr;
51
52 const auto parts = FileUtil::SplitPathComponents(stat.name);
53 const auto new_file = std::make_shared<VectorVfsFile>(buf, parts.back());
54
55 std::shared_ptr<VectorVfsDirectory> dtrv = out;
56 for (std::size_t j = 0; j < parts.size() - 1; ++j) {
57 if (dtrv == nullptr)
58 return nullptr;
59 const auto subdir = dtrv->GetSubdirectory(parts[j]);
60 if (subdir == nullptr) {
61 const auto temp = std::make_shared<VectorVfsDirectory>(
62 std::vector<VirtualFile>{}, std::vector<VirtualDir>{}, parts[j]);
63 dtrv->AddDirectory(temp);
64 dtrv = temp;
65 } else {
66 dtrv = std::dynamic_pointer_cast<VectorVfsDirectory>(subdir);
67 }
68 }
69
70 if (dtrv == nullptr)
71 return nullptr;
72 dtrv->AddFile(new_file);
73 }
74 }
75
76 return out;
77}
78
79} // namespace FileSys
diff --git a/src/core/file_sys/vfs_libzip.h b/src/core/file_sys/vfs_libzip.h
new file mode 100644
index 000000000..f68af576a
--- /dev/null
+++ b/src/core/file_sys/vfs_libzip.h
@@ -0,0 +1,13 @@
1// Copyright 2019 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 "core/file_sys/vfs_types.h"
8
9namespace FileSys {
10
11VirtualDir ExtractZIP(VirtualFile zip);
12
13} // namespace FileSys
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index a7c55e116..0c0f7ed6e 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -70,7 +70,7 @@ public:
70 70
71protected: 71protected:
72 void Get(Kernel::HLERequestContext& ctx) { 72 void Get(Kernel::HLERequestContext& ctx) {
73 LOG_INFO(Service_ACC, "called user_id={}", user_id.Format()); 73 LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
74 ProfileBase profile_base{}; 74 ProfileBase profile_base{};
75 ProfileData data{}; 75 ProfileData data{};
76 if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) { 76 if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) {
@@ -89,7 +89,7 @@ protected:
89 } 89 }
90 90
91 void GetBase(Kernel::HLERequestContext& ctx) { 91 void GetBase(Kernel::HLERequestContext& ctx) {
92 LOG_INFO(Service_ACC, "called user_id={}", user_id.Format()); 92 LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
93 ProfileBase profile_base{}; 93 ProfileBase profile_base{};
94 if (profile_manager.GetProfileBase(user_id, profile_base)) { 94 if (profile_manager.GetProfileBase(user_id, profile_base)) {
95 IPC::ResponseBuilder rb{ctx, 16}; 95 IPC::ResponseBuilder rb{ctx, 16};
@@ -263,7 +263,7 @@ private:
263}; 263};
264 264
265void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) { 265void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) {
266 LOG_INFO(Service_ACC, "called"); 266 LOG_DEBUG(Service_ACC, "called");
267 IPC::ResponseBuilder rb{ctx, 3}; 267 IPC::ResponseBuilder rb{ctx, 3};
268 rb.Push(RESULT_SUCCESS); 268 rb.Push(RESULT_SUCCESS);
269 rb.Push<u32>(static_cast<u32>(profile_manager->GetUserCount())); 269 rb.Push<u32>(static_cast<u32>(profile_manager->GetUserCount()));
@@ -272,7 +272,7 @@ void Module::Interface::GetUserCount(Kernel::HLERequestContext& ctx) {
272void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) { 272void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) {
273 IPC::RequestParser rp{ctx}; 273 IPC::RequestParser rp{ctx};
274 Common::UUID user_id = rp.PopRaw<Common::UUID>(); 274 Common::UUID user_id = rp.PopRaw<Common::UUID>();
275 LOG_INFO(Service_ACC, "called user_id={}", user_id.Format()); 275 LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
276 276
277 IPC::ResponseBuilder rb{ctx, 3}; 277 IPC::ResponseBuilder rb{ctx, 3};
278 rb.Push(RESULT_SUCCESS); 278 rb.Push(RESULT_SUCCESS);
@@ -280,21 +280,21 @@ void Module::Interface::GetUserExistence(Kernel::HLERequestContext& ctx) {
280} 280}
281 281
282void Module::Interface::ListAllUsers(Kernel::HLERequestContext& ctx) { 282void Module::Interface::ListAllUsers(Kernel::HLERequestContext& ctx) {
283 LOG_INFO(Service_ACC, "called"); 283 LOG_DEBUG(Service_ACC, "called");
284 ctx.WriteBuffer(profile_manager->GetAllUsers()); 284 ctx.WriteBuffer(profile_manager->GetAllUsers());
285 IPC::ResponseBuilder rb{ctx, 2}; 285 IPC::ResponseBuilder rb{ctx, 2};
286 rb.Push(RESULT_SUCCESS); 286 rb.Push(RESULT_SUCCESS);
287} 287}
288 288
289void Module::Interface::ListOpenUsers(Kernel::HLERequestContext& ctx) { 289void Module::Interface::ListOpenUsers(Kernel::HLERequestContext& ctx) {
290 LOG_INFO(Service_ACC, "called"); 290 LOG_DEBUG(Service_ACC, "called");
291 ctx.WriteBuffer(profile_manager->GetOpenUsers()); 291 ctx.WriteBuffer(profile_manager->GetOpenUsers());
292 IPC::ResponseBuilder rb{ctx, 2}; 292 IPC::ResponseBuilder rb{ctx, 2};
293 rb.Push(RESULT_SUCCESS); 293 rb.Push(RESULT_SUCCESS);
294} 294}
295 295
296void Module::Interface::GetLastOpenedUser(Kernel::HLERequestContext& ctx) { 296void Module::Interface::GetLastOpenedUser(Kernel::HLERequestContext& ctx) {
297 LOG_INFO(Service_ACC, "called"); 297 LOG_DEBUG(Service_ACC, "called");
298 IPC::ResponseBuilder rb{ctx, 6}; 298 IPC::ResponseBuilder rb{ctx, 6};
299 rb.Push(RESULT_SUCCESS); 299 rb.Push(RESULT_SUCCESS);
300 rb.PushRaw<Common::UUID>(profile_manager->GetLastOpenedUser()); 300 rb.PushRaw<Common::UUID>(profile_manager->GetLastOpenedUser());
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 6c594dcaf..34409e0c3 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -31,6 +31,7 @@
31#include "core/hle/service/am/tcap.h" 31#include "core/hle/service/am/tcap.h"
32#include "core/hle/service/apm/controller.h" 32#include "core/hle/service/apm/controller.h"
33#include "core/hle/service/apm/interface.h" 33#include "core/hle/service/apm/interface.h"
34#include "core/hle/service/bcat/backend/backend.h"
34#include "core/hle/service/filesystem/filesystem.h" 35#include "core/hle/service/filesystem/filesystem.h"
35#include "core/hle/service/ns/ns.h" 36#include "core/hle/service/ns/ns.h"
36#include "core/hle/service/nvflinger/nvflinger.h" 37#include "core/hle/service/nvflinger/nvflinger.h"
@@ -46,15 +47,20 @@ constexpr ResultCode ERR_NO_DATA_IN_CHANNEL{ErrorModule::AM, 0x2};
46constexpr ResultCode ERR_NO_MESSAGES{ErrorModule::AM, 0x3}; 47constexpr ResultCode ERR_NO_MESSAGES{ErrorModule::AM, 0x3};
47constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7}; 48constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7};
48 49
49constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA; 50enum class LaunchParameterKind : u32 {
51 ApplicationSpecific = 1,
52 AccountPreselectedUser = 2,
53};
54
55constexpr u32 LAUNCH_PARAMETER_ACCOUNT_PRESELECTED_USER_MAGIC = 0xC79497CA;
50 56
51struct LaunchParameters { 57struct LaunchParameterAccountPreselectedUser {
52 u32_le magic; 58 u32_le magic;
53 u32_le is_account_selected; 59 u32_le is_account_selected;
54 u128 current_user; 60 u128 current_user;
55 INSERT_PADDING_BYTES(0x70); 61 INSERT_PADDING_BYTES(0x70);
56}; 62};
57static_assert(sizeof(LaunchParameters) == 0x88); 63static_assert(sizeof(LaunchParameterAccountPreselectedUser) == 0x88);
58 64
59IWindowController::IWindowController(Core::System& system_) 65IWindowController::IWindowController(Core::System& system_)
60 : ServiceFramework("IWindowController"), system{system_} { 66 : ServiceFramework("IWindowController"), system{system_} {
@@ -232,12 +238,12 @@ IDebugFunctions::IDebugFunctions() : ServiceFramework{"IDebugFunctions"} {
232 238
233IDebugFunctions::~IDebugFunctions() = default; 239IDebugFunctions::~IDebugFunctions() = default;
234 240
235ISelfController::ISelfController(Core::System& system_, 241ISelfController::ISelfController(Core::System& system,
236 std::shared_ptr<NVFlinger::NVFlinger> nvflinger_) 242 std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
237 : ServiceFramework("ISelfController"), nvflinger(std::move(nvflinger_)) { 243 : ServiceFramework("ISelfController"), system(system), nvflinger(std::move(nvflinger)) {
238 // clang-format off 244 // clang-format off
239 static const FunctionInfo functions[] = { 245 static const FunctionInfo functions[] = {
240 {0, nullptr, "Exit"}, 246 {0, &ISelfController::Exit, "Exit"},
241 {1, &ISelfController::LockExit, "LockExit"}, 247 {1, &ISelfController::LockExit, "LockExit"},
242 {2, &ISelfController::UnlockExit, "UnlockExit"}, 248 {2, &ISelfController::UnlockExit, "UnlockExit"},
243 {3, &ISelfController::EnterFatalSection, "EnterFatalSection"}, 249 {3, &ISelfController::EnterFatalSection, "EnterFatalSection"},
@@ -282,7 +288,7 @@ ISelfController::ISelfController(Core::System& system_,
282 288
283 RegisterHandlers(functions); 289 RegisterHandlers(functions);
284 290
285 auto& kernel = system_.Kernel(); 291 auto& kernel = system.Kernel();
286 launchable_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual, 292 launchable_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
287 "ISelfController:LaunchableEvent"); 293 "ISelfController:LaunchableEvent");
288 294
@@ -298,15 +304,28 @@ ISelfController::ISelfController(Core::System& system_,
298 304
299ISelfController::~ISelfController() = default; 305ISelfController::~ISelfController() = default;
300 306
307void ISelfController::Exit(Kernel::HLERequestContext& ctx) {
308 LOG_DEBUG(Service_AM, "called");
309
310 system.Shutdown();
311
312 IPC::ResponseBuilder rb{ctx, 2};
313 rb.Push(RESULT_SUCCESS);
314}
315
301void ISelfController::LockExit(Kernel::HLERequestContext& ctx) { 316void ISelfController::LockExit(Kernel::HLERequestContext& ctx) {
302 LOG_WARNING(Service_AM, "(STUBBED) called"); 317 LOG_DEBUG(Service_AM, "called");
318
319 system.SetExitLock(true);
303 320
304 IPC::ResponseBuilder rb{ctx, 2}; 321 IPC::ResponseBuilder rb{ctx, 2};
305 rb.Push(RESULT_SUCCESS); 322 rb.Push(RESULT_SUCCESS);
306} 323}
307 324
308void ISelfController::UnlockExit(Kernel::HLERequestContext& ctx) { 325void ISelfController::UnlockExit(Kernel::HLERequestContext& ctx) {
309 LOG_WARNING(Service_AM, "(STUBBED) called"); 326 LOG_DEBUG(Service_AM, "called");
327
328 system.SetExitLock(false);
310 329
311 IPC::ResponseBuilder rb{ctx, 2}; 330 IPC::ResponseBuilder rb{ctx, 2};
312 rb.Push(RESULT_SUCCESS); 331 rb.Push(RESULT_SUCCESS);
@@ -550,6 +569,10 @@ void AppletMessageQueue::OperationModeChanged() {
550 on_operation_mode_changed.writable->Signal(); 569 on_operation_mode_changed.writable->Signal();
551} 570}
552 571
572void AppletMessageQueue::RequestExit() {
573 PushMessage(AppletMessage::ExitRequested);
574}
575
553ICommonStateGetter::ICommonStateGetter(Core::System& system, 576ICommonStateGetter::ICommonStateGetter(Core::System& system,
554 std::shared_ptr<AppletMessageQueue> msg_queue) 577 std::shared_ptr<AppletMessageQueue> msg_queue)
555 : ServiceFramework("ICommonStateGetter"), system(system), msg_queue(std::move(msg_queue)) { 578 : ServiceFramework("ICommonStateGetter"), system(system), msg_queue(std::move(msg_queue)) {
@@ -1066,7 +1089,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
1066 1089
1067 RegisterHandlers(functions); 1090 RegisterHandlers(functions);
1068 1091
1069 auto& kernel = Core::System::GetInstance().Kernel(); 1092 auto& kernel = system.Kernel();
1070 gpu_error_detected_event = Kernel::WritableEvent::CreateEventPair( 1093 gpu_error_detected_event = Kernel::WritableEvent::CreateEventPair(
1071 kernel, Kernel::ResetType::Manual, "IApplicationFunctions:GpuErrorDetectedSystemEvent"); 1094 kernel, Kernel::ResetType::Manual, "IApplicationFunctions:GpuErrorDetectedSystemEvent");
1072} 1095}
@@ -1111,26 +1134,55 @@ void IApplicationFunctions::EndBlockingHomeButton(Kernel::HLERequestContext& ctx
1111} 1134}
1112 1135
1113void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) { 1136void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
1114 LOG_DEBUG(Service_AM, "called"); 1137 IPC::RequestParser rp{ctx};
1138 const auto kind = rp.PopEnum<LaunchParameterKind>();
1115 1139
1116 LaunchParameters params{}; 1140 LOG_DEBUG(Service_AM, "called, kind={:08X}", static_cast<u8>(kind));
1117 1141
1118 params.magic = POP_LAUNCH_PARAMETER_MAGIC; 1142 if (kind == LaunchParameterKind::ApplicationSpecific && !launch_popped_application_specific) {
1119 params.is_account_selected = 1; 1143 const auto backend = BCAT::CreateBackendFromSettings(
1144 [this](u64 tid) { return system.GetFileSystemController().GetBCATDirectory(tid); });
1145 const auto build_id_full = Core::System::GetInstance().GetCurrentProcessBuildID();
1146 u64 build_id{};
1147 std::memcpy(&build_id, build_id_full.data(), sizeof(u64));
1120 1148
1121 Account::ProfileManager profile_manager{}; 1149 const auto data =
1122 const auto uuid = profile_manager.GetUser(Settings::values.current_user); 1150 backend->GetLaunchParameter({Core::CurrentProcess()->GetTitleID(), build_id});
1123 ASSERT(uuid);
1124 params.current_user = uuid->uuid;
1125 1151
1126 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 1152 if (data.has_value()) {
1153 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1154 rb.Push(RESULT_SUCCESS);
1155 rb.PushIpcInterface<AM::IStorage>(*data);
1156 launch_popped_application_specific = true;
1157 return;
1158 }
1159 } else if (kind == LaunchParameterKind::AccountPreselectedUser &&
1160 !launch_popped_account_preselect) {
1161 LaunchParameterAccountPreselectedUser params{};
1127 1162
1128 rb.Push(RESULT_SUCCESS); 1163 params.magic = LAUNCH_PARAMETER_ACCOUNT_PRESELECTED_USER_MAGIC;
1164 params.is_account_selected = 1;
1165
1166 Account::ProfileManager profile_manager{};
1167 const auto uuid = profile_manager.GetUser(Settings::values.current_user);
1168 ASSERT(uuid);
1169 params.current_user = uuid->uuid;
1129 1170
1130 std::vector<u8> buffer(sizeof(LaunchParameters)); 1171 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
1131 std::memcpy(buffer.data(), &params, buffer.size()); 1172
1173 rb.Push(RESULT_SUCCESS);
1174
1175 std::vector<u8> buffer(sizeof(LaunchParameterAccountPreselectedUser));
1176 std::memcpy(buffer.data(), &params, buffer.size());
1132 1177
1133 rb.PushIpcInterface<AM::IStorage>(buffer); 1178 rb.PushIpcInterface<AM::IStorage>(buffer);
1179 launch_popped_account_preselect = true;
1180 return;
1181 }
1182
1183 LOG_ERROR(Service_AM, "Attempted to load launch parameter but none was found!");
1184 IPC::ResponseBuilder rb{ctx, 2};
1185 rb.Push(ERR_NO_DATA_IN_CHANNEL);
1134} 1186}
1135 1187
1136void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest( 1188void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest(
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 28f870302..9169eb2bd 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -45,6 +45,7 @@ class AppletMessageQueue {
45public: 45public:
46 enum class AppletMessage : u32 { 46 enum class AppletMessage : u32 {
47 NoMessage = 0, 47 NoMessage = 0,
48 ExitRequested = 4,
48 FocusStateChanged = 15, 49 FocusStateChanged = 15,
49 OperationModeChanged = 30, 50 OperationModeChanged = 30,
50 PerformanceModeChanged = 31, 51 PerformanceModeChanged = 31,
@@ -59,6 +60,7 @@ public:
59 AppletMessage PopMessage(); 60 AppletMessage PopMessage();
60 std::size_t GetMessageCount() const; 61 std::size_t GetMessageCount() const;
61 void OperationModeChanged(); 62 void OperationModeChanged();
63 void RequestExit();
62 64
63private: 65private:
64 std::queue<AppletMessage> messages; 66 std::queue<AppletMessage> messages;
@@ -123,6 +125,7 @@ public:
123 ~ISelfController() override; 125 ~ISelfController() override;
124 126
125private: 127private:
128 void Exit(Kernel::HLERequestContext& ctx);
126 void LockExit(Kernel::HLERequestContext& ctx); 129 void LockExit(Kernel::HLERequestContext& ctx);
127 void UnlockExit(Kernel::HLERequestContext& ctx); 130 void UnlockExit(Kernel::HLERequestContext& ctx);
128 void EnterFatalSection(Kernel::HLERequestContext& ctx); 131 void EnterFatalSection(Kernel::HLERequestContext& ctx);
@@ -151,6 +154,8 @@ private:
151 u32 idle_time_detection_extension = 0; 154 u32 idle_time_detection_extension = 0;
152 u64 num_fatal_sections_entered = 0; 155 u64 num_fatal_sections_entered = 0;
153 bool is_auto_sleep_disabled = false; 156 bool is_auto_sleep_disabled = false;
157
158 Core::System& system;
154}; 159};
155 160
156class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> { 161class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> {
@@ -250,6 +255,8 @@ private:
250 void EnableApplicationCrashReport(Kernel::HLERequestContext& ctx); 255 void EnableApplicationCrashReport(Kernel::HLERequestContext& ctx);
251 void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx); 256 void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx);
252 257
258 bool launch_popped_application_specific = false;
259 bool launch_popped_account_preselect = false;
253 Kernel::EventPair gpu_error_detected_event; 260 Kernel::EventPair gpu_error_detected_event;
254 Core::System& system; 261 Core::System& system;
255}; 262};
diff --git a/src/core/hle/service/am/applet_ae.h b/src/core/hle/service/am/applet_ae.h
index 0e0d10858..2e3e45915 100644
--- a/src/core/hle/service/am/applet_ae.h
+++ b/src/core/hle/service/am/applet_ae.h
@@ -19,6 +19,8 @@ class NVFlinger;
19 19
20namespace AM { 20namespace AM {
21 21
22class AppletMessageQueue;
23
22class AppletAE final : public ServiceFramework<AppletAE> { 24class AppletAE final : public ServiceFramework<AppletAE> {
23public: 25public:
24 explicit AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, 26 explicit AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
diff --git a/src/core/hle/service/am/applet_oe.h b/src/core/hle/service/am/applet_oe.h
index 99a65e7b5..758da792d 100644
--- a/src/core/hle/service/am/applet_oe.h
+++ b/src/core/hle/service/am/applet_oe.h
@@ -19,6 +19,8 @@ class NVFlinger;
19 19
20namespace AM { 20namespace AM {
21 21
22class AppletMessageQueue;
23
22class AppletOE final : public ServiceFramework<AppletOE> { 24class AppletOE final : public ServiceFramework<AppletOE> {
23public: 25public:
24 explicit AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger, 26 explicit AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
index d2e35362f..720fe766f 100644
--- a/src/core/hle/service/am/applets/applets.cpp
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -157,6 +157,10 @@ AppletManager::AppletManager(Core::System& system_) : system{system_} {}
157 157
158AppletManager::~AppletManager() = default; 158AppletManager::~AppletManager() = default;
159 159
160const AppletFrontendSet& AppletManager::GetAppletFrontendSet() const {
161 return frontend;
162}
163
160void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) { 164void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) {
161 if (set.parental_controls != nullptr) 165 if (set.parental_controls != nullptr)
162 frontend.parental_controls = std::move(set.parental_controls); 166 frontend.parental_controls = std::move(set.parental_controls);
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h
index 764c3418c..226be88b1 100644
--- a/src/core/hle/service/am/applets/applets.h
+++ b/src/core/hle/service/am/applets/applets.h
@@ -190,6 +190,8 @@ public:
190 explicit AppletManager(Core::System& system_); 190 explicit AppletManager(Core::System& system_);
191 ~AppletManager(); 191 ~AppletManager();
192 192
193 const AppletFrontendSet& GetAppletFrontendSet() const;
194
193 void SetAppletFrontendSet(AppletFrontendSet set); 195 void SetAppletFrontendSet(AppletFrontendSet set);
194 void SetDefaultAppletFrontendSet(); 196 void SetDefaultAppletFrontendSet();
195 void SetDefaultAppletsIfMissing(); 197 void SetDefaultAppletsIfMissing();
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp
index d3e97776b..e9cf1e840 100644
--- a/src/core/hle/service/aoc/aoc_u.cpp
+++ b/src/core/hle/service/aoc/aoc_u.cpp
@@ -29,9 +29,9 @@ static bool CheckAOCTitleIDMatchesBase(u64 title_id, u64 base) {
29 return (title_id & DLC_BASE_TITLE_ID_MASK) == base; 29 return (title_id & DLC_BASE_TITLE_ID_MASK) == base;
30} 30}
31 31
32static std::vector<u64> AccumulateAOCTitleIDs() { 32static std::vector<u64> AccumulateAOCTitleIDs(Core::System& system) {
33 std::vector<u64> add_on_content; 33 std::vector<u64> add_on_content;
34 const auto& rcu = Core::System::GetInstance().GetContentProvider(); 34 const auto& rcu = system.GetContentProvider();
35 const auto list = 35 const auto list =
36 rcu.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); 36 rcu.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
37 std::transform(list.begin(), list.end(), std::back_inserter(add_on_content), 37 std::transform(list.begin(), list.end(), std::back_inserter(add_on_content),
@@ -47,7 +47,8 @@ static std::vector<u64> AccumulateAOCTitleIDs() {
47 return add_on_content; 47 return add_on_content;
48} 48}
49 49
50AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs()) { 50AOC_U::AOC_U(Core::System& system)
51 : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs(system)), system(system) {
51 // clang-format off 52 // clang-format off
52 static const FunctionInfo functions[] = { 53 static const FunctionInfo functions[] = {
53 {0, nullptr, "CountAddOnContentByApplicationId"}, 54 {0, nullptr, "CountAddOnContentByApplicationId"},
@@ -65,7 +66,7 @@ AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs
65 66
66 RegisterHandlers(functions); 67 RegisterHandlers(functions);
67 68
68 auto& kernel = Core::System::GetInstance().Kernel(); 69 auto& kernel = system.Kernel();
69 aoc_change_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual, 70 aoc_change_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
70 "GetAddOnContentListChanged:Event"); 71 "GetAddOnContentListChanged:Event");
71} 72}
@@ -86,7 +87,7 @@ void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) {
86 IPC::ResponseBuilder rb{ctx, 3}; 87 IPC::ResponseBuilder rb{ctx, 3};
87 rb.Push(RESULT_SUCCESS); 88 rb.Push(RESULT_SUCCESS);
88 89
89 const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID(); 90 const auto current = system.CurrentProcess()->GetTitleID();
90 91
91 const auto& disabled = Settings::values.disabled_addons[current]; 92 const auto& disabled = Settings::values.disabled_addons[current];
92 if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end()) { 93 if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end()) {
@@ -113,7 +114,7 @@ void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) {
113 LOG_DEBUG(Service_AOC, "called with offset={}, count={}, process_id={}", offset, count, 114 LOG_DEBUG(Service_AOC, "called with offset={}, count={}, process_id={}", offset, count,
114 process_id); 115 process_id);
115 116
116 const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID(); 117 const auto current = system.CurrentProcess()->GetTitleID();
117 118
118 std::vector<u32> out; 119 std::vector<u32> out;
119 const auto& disabled = Settings::values.disabled_addons[current]; 120 const auto& disabled = Settings::values.disabled_addons[current];
@@ -159,7 +160,7 @@ void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) {
159 IPC::ResponseBuilder rb{ctx, 4}; 160 IPC::ResponseBuilder rb{ctx, 4};
160 rb.Push(RESULT_SUCCESS); 161 rb.Push(RESULT_SUCCESS);
161 162
162 const auto title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID(); 163 const auto title_id = system.CurrentProcess()->GetTitleID();
163 FileSys::PatchManager pm{title_id}; 164 FileSys::PatchManager pm{title_id};
164 165
165 const auto res = pm.GetControlMetadata(); 166 const auto res = pm.GetControlMetadata();
@@ -196,8 +197,8 @@ void AOC_U::GetAddOnContentListChangedEvent(Kernel::HLERequestContext& ctx) {
196 rb.PushCopyObjects(aoc_change_event.readable); 197 rb.PushCopyObjects(aoc_change_event.readable);
197} 198}
198 199
199void InstallInterfaces(SM::ServiceManager& service_manager) { 200void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
200 std::make_shared<AOC_U>()->InstallAsService(service_manager); 201 std::make_shared<AOC_U>(system)->InstallAsService(service_manager);
201} 202}
202 203
203} // namespace Service::AOC 204} // namespace Service::AOC
diff --git a/src/core/hle/service/aoc/aoc_u.h b/src/core/hle/service/aoc/aoc_u.h
index 5effea730..848b2f416 100644
--- a/src/core/hle/service/aoc/aoc_u.h
+++ b/src/core/hle/service/aoc/aoc_u.h
@@ -14,7 +14,7 @@ namespace Service::AOC {
14 14
15class AOC_U final : public ServiceFramework<AOC_U> { 15class AOC_U final : public ServiceFramework<AOC_U> {
16public: 16public:
17 AOC_U(); 17 explicit AOC_U(Core::System& system);
18 ~AOC_U() override; 18 ~AOC_U() override;
19 19
20private: 20private:
@@ -26,9 +26,10 @@ private:
26 26
27 std::vector<u64> add_on_content; 27 std::vector<u64> add_on_content;
28 Kernel::EventPair aoc_change_event; 28 Kernel::EventPair aoc_change_event;
29 Core::System& system;
29}; 30};
30 31
31/// Registers all AOC services with the specified service manager. 32/// Registers all AOC services with the specified service manager.
32void InstallInterfaces(SM::ServiceManager& service_manager); 33void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
33 34
34} // namespace Service::AOC 35} // namespace Service::AOC
diff --git a/src/core/hle/service/bcat/backend/backend.cpp b/src/core/hle/service/bcat/backend/backend.cpp
new file mode 100644
index 000000000..9b677debe
--- /dev/null
+++ b/src/core/hle/service/bcat/backend/backend.cpp
@@ -0,0 +1,136 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/hex_util.h"
6#include "common/logging/log.h"
7#include "core/core.h"
8#include "core/hle/lock.h"
9#include "core/hle/service/bcat/backend/backend.h"
10
11namespace Service::BCAT {
12
13ProgressServiceBackend::ProgressServiceBackend(std::string event_name) : impl{} {
14 auto& kernel{Core::System::GetInstance().Kernel()};
15 event = Kernel::WritableEvent::CreateEventPair(
16 kernel, Kernel::ResetType::Automatic, "ProgressServiceBackend:UpdateEvent:" + event_name);
17}
18
19Kernel::SharedPtr<Kernel::ReadableEvent> ProgressServiceBackend::GetEvent() {
20 return event.readable;
21}
22
23DeliveryCacheProgressImpl& ProgressServiceBackend::GetImpl() {
24 return impl;
25}
26
27void ProgressServiceBackend::SetNeedHLELock(bool need) {
28 need_hle_lock = need;
29}
30
31void ProgressServiceBackend::SetTotalSize(u64 size) {
32 impl.total_bytes = size;
33 SignalUpdate();
34}
35
36void ProgressServiceBackend::StartConnecting() {
37 impl.status = DeliveryCacheProgressImpl::Status::Connecting;
38 SignalUpdate();
39}
40
41void ProgressServiceBackend::StartProcessingDataList() {
42 impl.status = DeliveryCacheProgressImpl::Status::ProcessingDataList;
43 SignalUpdate();
44}
45
46void ProgressServiceBackend::StartDownloadingFile(std::string_view dir_name,
47 std::string_view file_name, u64 file_size) {
48 impl.status = DeliveryCacheProgressImpl::Status::Downloading;
49 impl.current_downloaded_bytes = 0;
50 impl.current_total_bytes = file_size;
51 std::memcpy(impl.current_directory.data(), dir_name.data(),
52 std::min<u64>(dir_name.size(), 0x31ull));
53 std::memcpy(impl.current_file.data(), file_name.data(),
54 std::min<u64>(file_name.size(), 0x31ull));
55 SignalUpdate();
56}
57
58void ProgressServiceBackend::UpdateFileProgress(u64 downloaded) {
59 impl.current_downloaded_bytes = downloaded;
60 SignalUpdate();
61}
62
63void ProgressServiceBackend::FinishDownloadingFile() {
64 impl.total_downloaded_bytes += impl.current_total_bytes;
65 SignalUpdate();
66}
67
68void ProgressServiceBackend::CommitDirectory(std::string_view dir_name) {
69 impl.status = DeliveryCacheProgressImpl::Status::Committing;
70 impl.current_file.fill(0);
71 impl.current_downloaded_bytes = 0;
72 impl.current_total_bytes = 0;
73 std::memcpy(impl.current_directory.data(), dir_name.data(),
74 std::min<u64>(dir_name.size(), 0x31ull));
75 SignalUpdate();
76}
77
78void ProgressServiceBackend::FinishDownload(ResultCode result) {
79 impl.total_downloaded_bytes = impl.total_bytes;
80 impl.status = DeliveryCacheProgressImpl::Status::Done;
81 impl.result = result;
82 SignalUpdate();
83}
84
85void ProgressServiceBackend::SignalUpdate() const {
86 if (need_hle_lock) {
87 std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
88 event.writable->Signal();
89 } else {
90 event.writable->Signal();
91 }
92}
93
94Backend::Backend(DirectoryGetter getter) : dir_getter(std::move(getter)) {}
95
96Backend::~Backend() = default;
97
98NullBackend::NullBackend(const DirectoryGetter& getter) : Backend(std::move(getter)) {}
99
100NullBackend::~NullBackend() = default;
101
102bool NullBackend::Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) {
103 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}", title.title_id,
104 title.build_id);
105
106 progress.FinishDownload(RESULT_SUCCESS);
107 return true;
108}
109
110bool NullBackend::SynchronizeDirectory(TitleIDVersion title, std::string name,
111 ProgressServiceBackend& progress) {
112 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}, name={}", title.title_id,
113 title.build_id, name);
114
115 progress.FinishDownload(RESULT_SUCCESS);
116 return true;
117}
118
119bool NullBackend::Clear(u64 title_id) {
120 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}");
121
122 return true;
123}
124
125void NullBackend::SetPassphrase(u64 title_id, const Passphrase& passphrase) {
126 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase = {}", title_id,
127 Common::HexToString(passphrase));
128}
129
130std::optional<std::vector<u8>> NullBackend::GetLaunchParameter(TitleIDVersion title) {
131 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}", title.title_id,
132 title.build_id);
133 return std::nullopt;
134}
135
136} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/backend/backend.h b/src/core/hle/service/bcat/backend/backend.h
new file mode 100644
index 000000000..3f5d8b5dd
--- /dev/null
+++ b/src/core/hle/service/bcat/backend/backend.h
@@ -0,0 +1,147 @@
1// Copyright 2019 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 <functional>
8#include <optional>
9#include "common/common_types.h"
10#include "core/file_sys/vfs_types.h"
11#include "core/hle/kernel/readable_event.h"
12#include "core/hle/kernel/writable_event.h"
13#include "core/hle/result.h"
14
15namespace Service::BCAT {
16
17struct DeliveryCacheProgressImpl;
18
19using DirectoryGetter = std::function<FileSys::VirtualDir(u64)>;
20using Passphrase = std::array<u8, 0x20>;
21
22struct TitleIDVersion {
23 u64 title_id;
24 u64 build_id;
25};
26
27using DirectoryName = std::array<char, 0x20>;
28using FileName = std::array<char, 0x20>;
29
30struct DeliveryCacheProgressImpl {
31 enum class Status : s32 {
32 None = 0x0,
33 Queued = 0x1,
34 Connecting = 0x2,
35 ProcessingDataList = 0x3,
36 Downloading = 0x4,
37 Committing = 0x5,
38 Done = 0x9,
39 };
40
41 Status status;
42 ResultCode result = RESULT_SUCCESS;
43 DirectoryName current_directory;
44 FileName current_file;
45 s64 current_downloaded_bytes; ///< Bytes downloaded on current file.
46 s64 current_total_bytes; ///< Bytes total on current file.
47 s64 total_downloaded_bytes; ///< Bytes downloaded on overall download.
48 s64 total_bytes; ///< Bytes total on overall download.
49 INSERT_PADDING_BYTES(
50 0x198); ///< Appears to be unused in official code, possibly reserved for future use.
51};
52static_assert(sizeof(DeliveryCacheProgressImpl) == 0x200,
53 "DeliveryCacheProgressImpl has incorrect size.");
54
55// A class to manage the signalling to the game about BCAT download progress.
56// Some of this class is implemented in module.cpp to avoid exposing the implementation structure.
57class ProgressServiceBackend {
58 friend class IBcatService;
59
60public:
61 // Clients should call this with true if any of the functions are going to be called from a
62 // non-HLE thread and this class need to lock the hle mutex. (default is false)
63 void SetNeedHLELock(bool need);
64
65 // Sets the number of bytes total in the entire download.
66 void SetTotalSize(u64 size);
67
68 // Notifies the application that the backend has started connecting to the server.
69 void StartConnecting();
70 // Notifies the application that the backend has begun accumulating and processing metadata.
71 void StartProcessingDataList();
72
73 // Notifies the application that a file is starting to be downloaded.
74 void StartDownloadingFile(std::string_view dir_name, std::string_view file_name, u64 file_size);
75 // Updates the progress of the current file to the size passed.
76 void UpdateFileProgress(u64 downloaded);
77 // Notifies the application that the current file has completed download.
78 void FinishDownloadingFile();
79
80 // Notifies the application that all files in this directory have completed and are being
81 // finalized.
82 void CommitDirectory(std::string_view dir_name);
83
84 // Notifies the application that the operation completed with result code result.
85 void FinishDownload(ResultCode result);
86
87private:
88 explicit ProgressServiceBackend(std::string event_name);
89
90 Kernel::SharedPtr<Kernel::ReadableEvent> GetEvent();
91 DeliveryCacheProgressImpl& GetImpl();
92
93 void SignalUpdate() const;
94
95 DeliveryCacheProgressImpl impl;
96 Kernel::EventPair event;
97 bool need_hle_lock = false;
98};
99
100// A class representing an abstract backend for BCAT functionality.
101class Backend {
102public:
103 explicit Backend(DirectoryGetter getter);
104 virtual ~Backend();
105
106 // Called when the backend is needed to synchronize the data for the game with title ID and
107 // version in title. A ProgressServiceBackend object is provided to alert the application of
108 // status.
109 virtual bool Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) = 0;
110 // Very similar to Synchronize, but only for the directory provided. Backends should not alter
111 // the data for any other directories.
112 virtual bool SynchronizeDirectory(TitleIDVersion title, std::string name,
113 ProgressServiceBackend& progress) = 0;
114
115 // Removes all cached data associated with title id provided.
116 virtual bool Clear(u64 title_id) = 0;
117
118 // Sets the BCAT Passphrase to be used with the associated title ID.
119 virtual void SetPassphrase(u64 title_id, const Passphrase& passphrase) = 0;
120
121 // Gets the launch parameter used by AM associated with the title ID and version provided.
122 virtual std::optional<std::vector<u8>> GetLaunchParameter(TitleIDVersion title) = 0;
123
124protected:
125 DirectoryGetter dir_getter;
126};
127
128// A backend of BCAT that provides no operation.
129class NullBackend : public Backend {
130public:
131 explicit NullBackend(const DirectoryGetter& getter);
132 ~NullBackend() override;
133
134 bool Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) override;
135 bool SynchronizeDirectory(TitleIDVersion title, std::string name,
136 ProgressServiceBackend& progress) override;
137
138 bool Clear(u64 title_id) override;
139
140 void SetPassphrase(u64 title_id, const Passphrase& passphrase) override;
141
142 std::optional<std::vector<u8>> GetLaunchParameter(TitleIDVersion title) override;
143};
144
145std::unique_ptr<Backend> CreateBackendFromSettings(DirectoryGetter getter);
146
147} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/backend/boxcat.cpp b/src/core/hle/service/bcat/backend/boxcat.cpp
new file mode 100644
index 000000000..e6ee0810b
--- /dev/null
+++ b/src/core/hle/service/bcat/backend/boxcat.cpp
@@ -0,0 +1,503 @@
1// Copyright 2019 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <fmt/ostream.h>
6#include <httplib.h>
7#include <json.hpp>
8#include <mbedtls/sha256.h>
9#include "common/hex_util.h"
10#include "common/logging/backend.h"
11#include "common/logging/log.h"
12#include "core/core.h"
13#include "core/file_sys/vfs.h"
14#include "core/file_sys/vfs_libzip.h"
15#include "core/file_sys/vfs_vector.h"
16#include "core/frontend/applets/error.h"
17#include "core/hle/service/am/applets/applets.h"
18#include "core/hle/service/bcat/backend/boxcat.h"
19#include "core/settings.h"
20
21namespace {
22
23// Prevents conflicts with windows macro called CreateFile
24FileSys::VirtualFile VfsCreateFileWrap(FileSys::VirtualDir dir, std::string_view name) {
25 return dir->CreateFile(name);
26}
27
28// Prevents conflicts with windows macro called DeleteFile
29bool VfsDeleteFileWrap(FileSys::VirtualDir dir, std::string_view name) {
30 return dir->DeleteFile(name);
31}
32
33} // Anonymous namespace
34
35namespace Service::BCAT {
36
37constexpr ResultCode ERROR_GENERAL_BCAT_FAILURE{ErrorModule::BCAT, 1};
38
39constexpr char BOXCAT_HOSTNAME[] = "api.yuzu-emu.org";
40
41// Formatted using fmt with arg[0] = hex title id
42constexpr char BOXCAT_PATHNAME_DATA[] = "/game-assets/{:016X}/boxcat";
43constexpr char BOXCAT_PATHNAME_LAUNCHPARAM[] = "/game-assets/{:016X}/launchparam";
44
45constexpr char BOXCAT_PATHNAME_EVENTS[] = "/game-assets/boxcat/events";
46
47constexpr char BOXCAT_API_VERSION[] = "1";
48constexpr char BOXCAT_CLIENT_TYPE[] = "yuzu";
49
50// HTTP status codes for Boxcat
51enum class ResponseStatus {
52 Ok = 200, ///< Operation completed successfully.
53 BadClientVersion = 301, ///< The Boxcat-Client-Version doesn't match the server.
54 NoUpdate = 304, ///< The digest provided would match the new data, no need to update.
55 NoMatchTitleId = 404, ///< The title ID provided doesn't have a boxcat implementation.
56 NoMatchBuildId = 406, ///< The build ID provided is blacklisted (potentially because of format
57 ///< issues or whatnot) and has no data.
58};
59
60enum class DownloadResult {
61 Success = 0,
62 NoResponse,
63 GeneralWebError,
64 NoMatchTitleId,
65 NoMatchBuildId,
66 InvalidContentType,
67 GeneralFSError,
68 BadClientVersion,
69};
70
71constexpr std::array<const char*, 8> DOWNLOAD_RESULT_LOG_MESSAGES{
72 "Success",
73 "There was no response from the server.",
74 "There was a general web error code returned from the server.",
75 "The title ID of the current game doesn't have a boxcat implementation. If you believe an "
76 "implementation should be added, contact yuzu support.",
77 "The build ID of the current version of the game is marked as incompatible with the current "
78 "BCAT distribution. Try upgrading or downgrading your game version or contacting yuzu support.",
79 "The content type of the web response was invalid.",
80 "There was a general filesystem error while saving the zip file.",
81 "The server is either too new or too old to serve the request. Try using the latest version of "
82 "an official release of yuzu.",
83};
84
85std::ostream& operator<<(std::ostream& os, DownloadResult result) {
86 return os << DOWNLOAD_RESULT_LOG_MESSAGES.at(static_cast<std::size_t>(result));
87}
88
89constexpr u32 PORT = 443;
90constexpr u32 TIMEOUT_SECONDS = 30;
91constexpr u64 VFS_COPY_BLOCK_SIZE = 1ull << 24; // 4MB
92
93namespace {
94
95std::string GetBINFilePath(u64 title_id) {
96 return fmt::format("{}bcat/{:016X}/launchparam.bin",
97 FileUtil::GetUserPath(FileUtil::UserPath::CacheDir), title_id);
98}
99
100std::string GetZIPFilePath(u64 title_id) {
101 return fmt::format("{}bcat/{:016X}/data.zip",
102 FileUtil::GetUserPath(FileUtil::UserPath::CacheDir), title_id);
103}
104
105// If the error is something the user should know about (build ID mismatch, bad client version),
106// display an error.
107void HandleDownloadDisplayResult(DownloadResult res) {
108 if (res == DownloadResult::Success || res == DownloadResult::NoResponse ||
109 res == DownloadResult::GeneralWebError || res == DownloadResult::GeneralFSError ||
110 res == DownloadResult::NoMatchTitleId || res == DownloadResult::InvalidContentType) {
111 return;
112 }
113
114 const auto& frontend{Core::System::GetInstance().GetAppletManager().GetAppletFrontendSet()};
115 frontend.error->ShowCustomErrorText(
116 ResultCode(-1), "There was an error while attempting to use Boxcat.",
117 DOWNLOAD_RESULT_LOG_MESSAGES[static_cast<std::size_t>(res)], [] {});
118}
119
120bool VfsRawCopyProgress(FileSys::VirtualFile src, FileSys::VirtualFile dest,
121 std::string_view dir_name, ProgressServiceBackend& progress,
122 std::size_t block_size = 0x1000) {
123 if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
124 return false;
125 if (!dest->Resize(src->GetSize()))
126 return false;
127
128 progress.StartDownloadingFile(dir_name, src->GetName(), src->GetSize());
129
130 std::vector<u8> temp(std::min(block_size, src->GetSize()));
131 for (std::size_t i = 0; i < src->GetSize(); i += block_size) {
132 const auto read = std::min(block_size, src->GetSize() - i);
133
134 if (src->Read(temp.data(), read, i) != read) {
135 return false;
136 }
137
138 if (dest->Write(temp.data(), read, i) != read) {
139 return false;
140 }
141
142 progress.UpdateFileProgress(i);
143 }
144
145 progress.FinishDownloadingFile();
146
147 return true;
148}
149
150bool VfsRawCopyDProgressSingle(FileSys::VirtualDir src, FileSys::VirtualDir dest,
151 ProgressServiceBackend& progress, std::size_t block_size = 0x1000) {
152 if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
153 return false;
154
155 for (const auto& file : src->GetFiles()) {
156 const auto out_file = VfsCreateFileWrap(dest, file->GetName());
157 if (!VfsRawCopyProgress(file, out_file, src->GetName(), progress, block_size)) {
158 return false;
159 }
160 }
161 progress.CommitDirectory(src->GetName());
162
163 return true;
164}
165
166bool VfsRawCopyDProgress(FileSys::VirtualDir src, FileSys::VirtualDir dest,
167 ProgressServiceBackend& progress, std::size_t block_size = 0x1000) {
168 if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
169 return false;
170
171 for (const auto& dir : src->GetSubdirectories()) {
172 const auto out = dest->CreateSubdirectory(dir->GetName());
173 if (!VfsRawCopyDProgressSingle(dir, out, progress, block_size)) {
174 return false;
175 }
176 }
177
178 return true;
179}
180
181} // Anonymous namespace
182
183class Boxcat::Client {
184public:
185 Client(std::string path, u64 title_id, u64 build_id)
186 : path(std::move(path)), title_id(title_id), build_id(build_id) {}
187
188 DownloadResult DownloadDataZip() {
189 return DownloadInternal(fmt::format(BOXCAT_PATHNAME_DATA, title_id), TIMEOUT_SECONDS,
190 "application/zip");
191 }
192
193 DownloadResult DownloadLaunchParam() {
194 return DownloadInternal(fmt::format(BOXCAT_PATHNAME_LAUNCHPARAM, title_id),
195 TIMEOUT_SECONDS / 3, "application/octet-stream");
196 }
197
198private:
199 DownloadResult DownloadInternal(const std::string& resolved_path, u32 timeout_seconds,
200 const std::string& content_type_name) {
201 if (client == nullptr) {
202 client = std::make_unique<httplib::SSLClient>(BOXCAT_HOSTNAME, PORT, timeout_seconds);
203 }
204
205 httplib::Headers headers{
206 {std::string("Game-Assets-API-Version"), std::string(BOXCAT_API_VERSION)},
207 {std::string("Boxcat-Client-Type"), std::string(BOXCAT_CLIENT_TYPE)},
208 {std::string("Game-Build-Id"), fmt::format("{:016X}", build_id)},
209 };
210
211 if (FileUtil::Exists(path)) {
212 FileUtil::IOFile file{path, "rb"};
213 if (file.IsOpen()) {
214 std::vector<u8> bytes(file.GetSize());
215 file.ReadBytes(bytes.data(), bytes.size());
216 const auto digest = DigestFile(bytes);
217 headers.insert({std::string("If-None-Match"), Common::HexToString(digest, false)});
218 }
219 }
220
221 const auto response = client->Get(resolved_path.c_str(), headers);
222 if (response == nullptr)
223 return DownloadResult::NoResponse;
224
225 if (response->status == static_cast<int>(ResponseStatus::NoUpdate))
226 return DownloadResult::Success;
227 if (response->status == static_cast<int>(ResponseStatus::BadClientVersion))
228 return DownloadResult::BadClientVersion;
229 if (response->status == static_cast<int>(ResponseStatus::NoMatchTitleId))
230 return DownloadResult::NoMatchTitleId;
231 if (response->status == static_cast<int>(ResponseStatus::NoMatchBuildId))
232 return DownloadResult::NoMatchBuildId;
233 if (response->status != static_cast<int>(ResponseStatus::Ok))
234 return DownloadResult::GeneralWebError;
235
236 const auto content_type = response->headers.find("content-type");
237 if (content_type == response->headers.end() ||
238 content_type->second.find(content_type_name) == std::string::npos) {
239 return DownloadResult::InvalidContentType;
240 }
241
242 FileUtil::CreateFullPath(path);
243 FileUtil::IOFile file{path, "wb"};
244 if (!file.IsOpen())
245 return DownloadResult::GeneralFSError;
246 if (!file.Resize(response->body.size()))
247 return DownloadResult::GeneralFSError;
248 if (file.WriteBytes(response->body.data(), response->body.size()) != response->body.size())
249 return DownloadResult::GeneralFSError;
250
251 return DownloadResult::Success;
252 }
253
254 using Digest = std::array<u8, 0x20>;
255 static Digest DigestFile(std::vector<u8> bytes) {
256 Digest out{};
257 mbedtls_sha256(bytes.data(), bytes.size(), out.data(), 0);
258 return out;
259 }
260
261 std::unique_ptr<httplib::Client> client;
262 std::string path;
263 u64 title_id;
264 u64 build_id;
265};
266
267Boxcat::Boxcat(DirectoryGetter getter) : Backend(std::move(getter)) {}
268
269Boxcat::~Boxcat() = default;
270
271void SynchronizeInternal(DirectoryGetter dir_getter, TitleIDVersion title,
272 ProgressServiceBackend& progress,
273 std::optional<std::string> dir_name = {}) {
274 progress.SetNeedHLELock(true);
275
276 if (Settings::values.bcat_boxcat_local) {
277 LOG_INFO(Service_BCAT, "Boxcat using local data by override, skipping download.");
278 const auto dir = dir_getter(title.title_id);
279 if (dir)
280 progress.SetTotalSize(dir->GetSize());
281 progress.FinishDownload(RESULT_SUCCESS);
282 return;
283 }
284
285 const auto zip_path{GetZIPFilePath(title.title_id)};
286 Boxcat::Client client{zip_path, title.title_id, title.build_id};
287
288 progress.StartConnecting();
289
290 const auto res = client.DownloadDataZip();
291 if (res != DownloadResult::Success) {
292 LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res);
293
294 if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) {
295 FileUtil::Delete(zip_path);
296 }
297
298 HandleDownloadDisplayResult(res);
299 progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE);
300 return;
301 }
302
303 progress.StartProcessingDataList();
304
305 FileUtil::IOFile zip{zip_path, "rb"};
306 const auto size = zip.GetSize();
307 std::vector<u8> bytes(size);
308 if (!zip.IsOpen() || size == 0 || zip.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) {
309 LOG_ERROR(Service_BCAT, "Boxcat failed to read ZIP file at path '{}'!", zip_path);
310 progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE);
311 return;
312 }
313
314 const auto extracted = FileSys::ExtractZIP(std::make_shared<FileSys::VectorVfsFile>(bytes));
315 if (extracted == nullptr) {
316 LOG_ERROR(Service_BCAT, "Boxcat failed to extract ZIP file!");
317 progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE);
318 return;
319 }
320
321 if (dir_name == std::nullopt) {
322 progress.SetTotalSize(extracted->GetSize());
323
324 const auto target_dir = dir_getter(title.title_id);
325 if (target_dir == nullptr || !VfsRawCopyDProgress(extracted, target_dir, progress)) {
326 LOG_ERROR(Service_BCAT, "Boxcat failed to copy extracted ZIP to target directory!");
327 progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE);
328 return;
329 }
330 } else {
331 const auto target_dir = dir_getter(title.title_id);
332 if (target_dir == nullptr) {
333 LOG_ERROR(Service_BCAT, "Boxcat failed to get directory for title ID!");
334 progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE);
335 return;
336 }
337
338 const auto target_sub = target_dir->GetSubdirectory(*dir_name);
339 const auto source_sub = extracted->GetSubdirectory(*dir_name);
340
341 progress.SetTotalSize(source_sub->GetSize());
342
343 std::vector<std::string> filenames;
344 {
345 const auto files = target_sub->GetFiles();
346 std::transform(files.begin(), files.end(), std::back_inserter(filenames),
347 [](const auto& vfile) { return vfile->GetName(); });
348 }
349
350 for (const auto& filename : filenames) {
351 VfsDeleteFileWrap(target_sub, filename);
352 }
353
354 if (target_sub == nullptr || source_sub == nullptr ||
355 !VfsRawCopyDProgressSingle(source_sub, target_sub, progress)) {
356 LOG_ERROR(Service_BCAT, "Boxcat failed to copy extracted ZIP to target directory!");
357 progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE);
358 return;
359 }
360 }
361
362 progress.FinishDownload(RESULT_SUCCESS);
363}
364
365bool Boxcat::Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) {
366 is_syncing.exchange(true);
367 std::thread([this, title, &progress] { SynchronizeInternal(dir_getter, title, progress); })
368 .detach();
369 return true;
370}
371
372bool Boxcat::SynchronizeDirectory(TitleIDVersion title, std::string name,
373 ProgressServiceBackend& progress) {
374 is_syncing.exchange(true);
375 std::thread(
376 [this, title, name, &progress] { SynchronizeInternal(dir_getter, title, progress, name); })
377 .detach();
378 return true;
379}
380
381bool Boxcat::Clear(u64 title_id) {
382 if (Settings::values.bcat_boxcat_local) {
383 LOG_INFO(Service_BCAT, "Boxcat using local data by override, skipping clear.");
384 return true;
385 }
386
387 const auto dir = dir_getter(title_id);
388
389 std::vector<std::string> dirnames;
390
391 for (const auto& subdir : dir->GetSubdirectories())
392 dirnames.push_back(subdir->GetName());
393
394 for (const auto& subdir : dirnames) {
395 if (!dir->DeleteSubdirectoryRecursive(subdir))
396 return false;
397 }
398
399 return true;
400}
401
402void Boxcat::SetPassphrase(u64 title_id, const Passphrase& passphrase) {
403 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase={}", title_id,
404 Common::HexToString(passphrase));
405}
406
407std::optional<std::vector<u8>> Boxcat::GetLaunchParameter(TitleIDVersion title) {
408 const auto path{GetBINFilePath(title.title_id)};
409
410 if (Settings::values.bcat_boxcat_local) {
411 LOG_INFO(Service_BCAT, "Boxcat using local data by override, skipping download.");
412 } else {
413 Boxcat::Client client{path, title.title_id, title.build_id};
414
415 const auto res = client.DownloadLaunchParam();
416 if (res != DownloadResult::Success) {
417 LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res);
418
419 if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) {
420 FileUtil::Delete(path);
421 }
422
423 HandleDownloadDisplayResult(res);
424 return std::nullopt;
425 }
426 }
427
428 FileUtil::IOFile bin{path, "rb"};
429 const auto size = bin.GetSize();
430 std::vector<u8> bytes(size);
431 if (!bin.IsOpen() || size == 0 || bin.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) {
432 LOG_ERROR(Service_BCAT, "Boxcat failed to read launch parameter binary at path '{}'!",
433 path);
434 return std::nullopt;
435 }
436
437 return bytes;
438}
439
440Boxcat::StatusResult Boxcat::GetStatus(std::optional<std::string>& global,
441 std::map<std::string, EventStatus>& games) {
442 httplib::SSLClient client{BOXCAT_HOSTNAME, static_cast<int>(PORT),
443 static_cast<int>(TIMEOUT_SECONDS)};
444
445 httplib::Headers headers{
446 {std::string("Game-Assets-API-Version"), std::string(BOXCAT_API_VERSION)},
447 {std::string("Boxcat-Client-Type"), std::string(BOXCAT_CLIENT_TYPE)},
448 };
449
450 const auto response = client.Get(BOXCAT_PATHNAME_EVENTS, headers);
451 if (response == nullptr)
452 return StatusResult::Offline;
453
454 if (response->status == static_cast<int>(ResponseStatus::BadClientVersion))
455 return StatusResult::BadClientVersion;
456
457 try {
458 nlohmann::json json = nlohmann::json::parse(response->body);
459
460 if (!json["online"].get<bool>())
461 return StatusResult::Offline;
462
463 if (json["global"].is_null())
464 global = std::nullopt;
465 else
466 global = json["global"].get<std::string>();
467
468 if (json["games"].is_array()) {
469 for (const auto object : json["games"]) {
470 if (object.is_object() && object.find("name") != object.end()) {
471 EventStatus detail{};
472 if (object["header"].is_string()) {
473 detail.header = object["header"].get<std::string>();
474 } else {
475 detail.header = std::nullopt;
476 }
477
478 if (object["footer"].is_string()) {
479 detail.footer = object["footer"].get<std::string>();
480 } else {
481 detail.footer = std::nullopt;
482 }
483
484 if (object["events"].is_array()) {
485 for (const auto& event : object["events"]) {
486 if (!event.is_string())
487 continue;
488 detail.events.push_back(event.get<std::string>());
489 }
490 }
491
492 games.insert_or_assign(object["name"], std::move(detail));
493 }
494 }
495 }
496
497 return StatusResult::Success;
498 } catch (const nlohmann::json::parse_error& e) {
499 return StatusResult::ParseError;
500 }
501}
502
503} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/backend/boxcat.h b/src/core/hle/service/bcat/backend/boxcat.h
new file mode 100644
index 000000000..601151189
--- /dev/null
+++ b/src/core/hle/service/bcat/backend/boxcat.h
@@ -0,0 +1,58 @@
1// Copyright 2019 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 <atomic>
8#include <map>
9#include <optional>
10#include "core/hle/service/bcat/backend/backend.h"
11
12namespace Service::BCAT {
13
14struct EventStatus {
15 std::optional<std::string> header;
16 std::optional<std::string> footer;
17 std::vector<std::string> events;
18};
19
20/// Boxcat is yuzu's custom backend implementation of Nintendo's BCAT service. It is free to use and
21/// doesn't require a switch or nintendo account. The content is controlled by the yuzu team.
22class Boxcat final : public Backend {
23 friend void SynchronizeInternal(DirectoryGetter dir_getter, TitleIDVersion title,
24 ProgressServiceBackend& progress,
25 std::optional<std::string> dir_name);
26
27public:
28 explicit Boxcat(DirectoryGetter getter);
29 ~Boxcat() override;
30
31 bool Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) override;
32 bool SynchronizeDirectory(TitleIDVersion title, std::string name,
33 ProgressServiceBackend& progress) override;
34
35 bool Clear(u64 title_id) override;
36
37 void SetPassphrase(u64 title_id, const Passphrase& passphrase) override;
38
39 std::optional<std::vector<u8>> GetLaunchParameter(TitleIDVersion title) override;
40
41 enum class StatusResult {
42 Success,
43 Offline,
44 ParseError,
45 BadClientVersion,
46 };
47
48 static StatusResult GetStatus(std::optional<std::string>& global,
49 std::map<std::string, EventStatus>& games);
50
51private:
52 std::atomic_bool is_syncing{false};
53
54 class Client;
55 std::unique_ptr<Client> client;
56};
57
58} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/bcat.cpp b/src/core/hle/service/bcat/bcat.cpp
index 179aa4949..c2f946424 100644
--- a/src/core/hle/service/bcat/bcat.cpp
+++ b/src/core/hle/service/bcat/bcat.cpp
@@ -6,11 +6,15 @@
6 6
7namespace Service::BCAT { 7namespace Service::BCAT {
8 8
9BCAT::BCAT(std::shared_ptr<Module> module, const char* name) 9BCAT::BCAT(std::shared_ptr<Module> module, FileSystem::FileSystemController& fsc, const char* name)
10 : Module::Interface(std::move(module), name) { 10 : Module::Interface(std::move(module), fsc, name) {
11 // clang-format off
11 static const FunctionInfo functions[] = { 12 static const FunctionInfo functions[] = {
12 {0, &BCAT::CreateBcatService, "CreateBcatService"}, 13 {0, &BCAT::CreateBcatService, "CreateBcatService"},
14 {1, &BCAT::CreateDeliveryCacheStorageService, "CreateDeliveryCacheStorageService"},
15 {2, &BCAT::CreateDeliveryCacheStorageServiceWithApplicationId, "CreateDeliveryCacheStorageServiceWithApplicationId"},
13 }; 16 };
17 // clang-format on
14 RegisterHandlers(functions); 18 RegisterHandlers(functions);
15} 19}
16 20
diff --git a/src/core/hle/service/bcat/bcat.h b/src/core/hle/service/bcat/bcat.h
index 802bd689a..813073658 100644
--- a/src/core/hle/service/bcat/bcat.h
+++ b/src/core/hle/service/bcat/bcat.h
@@ -10,7 +10,8 @@ namespace Service::BCAT {
10 10
11class BCAT final : public Module::Interface { 11class BCAT final : public Module::Interface {
12public: 12public:
13 explicit BCAT(std::shared_ptr<Module> module, const char* name); 13 explicit BCAT(std::shared_ptr<Module> module, FileSystem::FileSystemController& fsc,
14 const char* name);
14 ~BCAT() override; 15 ~BCAT() override;
15}; 16};
16 17
diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp
index b7bd738fc..b3fed56c7 100644
--- a/src/core/hle/service/bcat/module.cpp
+++ b/src/core/hle/service/bcat/module.cpp
@@ -2,34 +2,254 @@
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 <cctype>
6#include <mbedtls/md5.h>
7#include "backend/boxcat.h"
8#include "common/hex_util.h"
5#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "common/string_util.h"
11#include "core/file_sys/vfs.h"
6#include "core/hle/ipc_helpers.h" 12#include "core/hle/ipc_helpers.h"
13#include "core/hle/kernel/process.h"
14#include "core/hle/kernel/readable_event.h"
15#include "core/hle/kernel/writable_event.h"
16#include "core/hle/service/bcat/backend/backend.h"
7#include "core/hle/service/bcat/bcat.h" 17#include "core/hle/service/bcat/bcat.h"
8#include "core/hle/service/bcat/module.h" 18#include "core/hle/service/bcat/module.h"
19#include "core/hle/service/filesystem/filesystem.h"
20#include "core/settings.h"
9 21
10namespace Service::BCAT { 22namespace Service::BCAT {
11 23
24constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::BCAT, 1};
25constexpr ResultCode ERROR_FAILED_OPEN_ENTITY{ErrorModule::BCAT, 2};
26constexpr ResultCode ERROR_ENTITY_ALREADY_OPEN{ErrorModule::BCAT, 6};
27constexpr ResultCode ERROR_NO_OPEN_ENTITY{ErrorModule::BCAT, 7};
28
29// The command to clear the delivery cache just calls fs IFileSystem DeleteFile on all of the files
30// and if any of them have a non-zero result it just forwards that result. This is the FS error code
31// for permission denied, which is the closest approximation of this scenario.
32constexpr ResultCode ERROR_FAILED_CLEAR_CACHE{ErrorModule::FS, 6400};
33
34using BCATDigest = std::array<u8, 0x10>;
35
36namespace {
37
38u64 GetCurrentBuildID() {
39 const auto& id = Core::System::GetInstance().GetCurrentProcessBuildID();
40 u64 out{};
41 std::memcpy(&out, id.data(), sizeof(u64));
42 return out;
43}
44
45// The digest is only used to determine if a file is unique compared to others of the same name.
46// Since the algorithm isn't ever checked in game, MD5 is safe.
47BCATDigest DigestFile(const FileSys::VirtualFile& file) {
48 BCATDigest out{};
49 const auto bytes = file->ReadAllBytes();
50 mbedtls_md5(bytes.data(), bytes.size(), out.data());
51 return out;
52}
53
54// For a name to be valid it must be non-empty, must have a null terminating character as the final
55// char, can only contain numbers, letters, underscores and a hyphen if directory and a period if
56// file.
57bool VerifyNameValidInternal(Kernel::HLERequestContext& ctx, std::array<char, 0x20> name,
58 char match_char) {
59 const auto null_chars = std::count(name.begin(), name.end(), 0);
60 const auto bad_chars = std::count_if(name.begin(), name.end(), [match_char](char c) {
61 return !std::isalnum(static_cast<u8>(c)) && c != '_' && c != match_char && c != '\0';
62 });
63 if (null_chars == 0x20 || null_chars == 0 || bad_chars != 0 || name[0x1F] != '\0') {
64 LOG_ERROR(Service_BCAT, "Name passed was invalid!");
65 IPC::ResponseBuilder rb{ctx, 2};
66 rb.Push(ERROR_INVALID_ARGUMENT);
67 return false;
68 }
69
70 return true;
71}
72
73bool VerifyNameValidDir(Kernel::HLERequestContext& ctx, DirectoryName name) {
74 return VerifyNameValidInternal(ctx, name, '-');
75}
76
77bool VerifyNameValidFile(Kernel::HLERequestContext& ctx, FileName name) {
78 return VerifyNameValidInternal(ctx, name, '.');
79}
80
81} // Anonymous namespace
82
83struct DeliveryCacheDirectoryEntry {
84 FileName name;
85 u64 size;
86 BCATDigest digest;
87};
88
89class IDeliveryCacheProgressService final : public ServiceFramework<IDeliveryCacheProgressService> {
90public:
91 IDeliveryCacheProgressService(Kernel::SharedPtr<Kernel::ReadableEvent> event,
92 const DeliveryCacheProgressImpl& impl)
93 : ServiceFramework{"IDeliveryCacheProgressService"}, event(std::move(event)), impl(impl) {
94 // clang-format off
95 static const FunctionInfo functions[] = {
96 {0, &IDeliveryCacheProgressService::GetEvent, "GetEvent"},
97 {1, &IDeliveryCacheProgressService::GetImpl, "GetImpl"},
98 };
99 // clang-format on
100
101 RegisterHandlers(functions);
102 }
103
104private:
105 void GetEvent(Kernel::HLERequestContext& ctx) {
106 LOG_DEBUG(Service_BCAT, "called");
107
108 IPC::ResponseBuilder rb{ctx, 2, 1};
109 rb.Push(RESULT_SUCCESS);
110 rb.PushCopyObjects(event);
111 }
112
113 void GetImpl(Kernel::HLERequestContext& ctx) {
114 LOG_DEBUG(Service_BCAT, "called");
115
116 ctx.WriteBuffer(&impl, sizeof(DeliveryCacheProgressImpl));
117
118 IPC::ResponseBuilder rb{ctx, 2};
119 rb.Push(RESULT_SUCCESS);
120 }
121
122 Kernel::SharedPtr<Kernel::ReadableEvent> event;
123 const DeliveryCacheProgressImpl& impl;
124};
125
12class IBcatService final : public ServiceFramework<IBcatService> { 126class IBcatService final : public ServiceFramework<IBcatService> {
13public: 127public:
14 IBcatService() : ServiceFramework("IBcatService") { 128 IBcatService(Backend& backend) : ServiceFramework("IBcatService"), backend(backend) {
129 // clang-format off
15 static const FunctionInfo functions[] = { 130 static const FunctionInfo functions[] = {
16 {10100, nullptr, "RequestSyncDeliveryCache"}, 131 {10100, &IBcatService::RequestSyncDeliveryCache, "RequestSyncDeliveryCache"},
17 {10101, nullptr, "RequestSyncDeliveryCacheWithDirectoryName"}, 132 {10101, &IBcatService::RequestSyncDeliveryCacheWithDirectoryName, "RequestSyncDeliveryCacheWithDirectoryName"},
18 {10200, nullptr, "CancelSyncDeliveryCacheRequest"}, 133 {10200, nullptr, "CancelSyncDeliveryCacheRequest"},
19 {20100, nullptr, "RequestSyncDeliveryCacheWithApplicationId"}, 134 {20100, nullptr, "RequestSyncDeliveryCacheWithApplicationId"},
20 {20101, nullptr, "RequestSyncDeliveryCacheWithApplicationIdAndDirectoryName"}, 135 {20101, nullptr, "RequestSyncDeliveryCacheWithApplicationIdAndDirectoryName"},
21 {30100, nullptr, "SetPassphrase"}, 136 {30100, &IBcatService::SetPassphrase, "SetPassphrase"},
22 {30200, nullptr, "RegisterBackgroundDeliveryTask"}, 137 {30200, nullptr, "RegisterBackgroundDeliveryTask"},
23 {30201, nullptr, "UnregisterBackgroundDeliveryTask"}, 138 {30201, nullptr, "UnregisterBackgroundDeliveryTask"},
24 {30202, nullptr, "BlockDeliveryTask"}, 139 {30202, nullptr, "BlockDeliveryTask"},
25 {30203, nullptr, "UnblockDeliveryTask"}, 140 {30203, nullptr, "UnblockDeliveryTask"},
26 {90100, nullptr, "EnumerateBackgroundDeliveryTask"}, 141 {90100, nullptr, "EnumerateBackgroundDeliveryTask"},
27 {90200, nullptr, "GetDeliveryList"}, 142 {90200, nullptr, "GetDeliveryList"},
28 {90201, nullptr, "ClearDeliveryCacheStorage"}, 143 {90201, &IBcatService::ClearDeliveryCacheStorage, "ClearDeliveryCacheStorage"},
29 {90300, nullptr, "GetPushNotificationLog"}, 144 {90300, nullptr, "GetPushNotificationLog"},
30 }; 145 };
146 // clang-format on
31 RegisterHandlers(functions); 147 RegisterHandlers(functions);
32 } 148 }
149
150private:
151 enum class SyncType {
152 Normal,
153 Directory,
154 Count,
155 };
156
157 std::shared_ptr<IDeliveryCacheProgressService> CreateProgressService(SyncType type) {
158 auto& backend{progress.at(static_cast<std::size_t>(type))};
159 return std::make_shared<IDeliveryCacheProgressService>(backend.GetEvent(),
160 backend.GetImpl());
161 }
162
163 void RequestSyncDeliveryCache(Kernel::HLERequestContext& ctx) {
164 LOG_DEBUG(Service_BCAT, "called");
165
166 backend.Synchronize({Core::CurrentProcess()->GetTitleID(), GetCurrentBuildID()},
167 progress.at(static_cast<std::size_t>(SyncType::Normal)));
168
169 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
170 rb.Push(RESULT_SUCCESS);
171 rb.PushIpcInterface(CreateProgressService(SyncType::Normal));
172 }
173
174 void RequestSyncDeliveryCacheWithDirectoryName(Kernel::HLERequestContext& ctx) {
175 IPC::RequestParser rp{ctx};
176 const auto name_raw = rp.PopRaw<DirectoryName>();
177 const auto name =
178 Common::StringFromFixedZeroTerminatedBuffer(name_raw.data(), name_raw.size());
179
180 LOG_DEBUG(Service_BCAT, "called, name={}", name);
181
182 backend.SynchronizeDirectory({Core::CurrentProcess()->GetTitleID(), GetCurrentBuildID()},
183 name,
184 progress.at(static_cast<std::size_t>(SyncType::Directory)));
185
186 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
187 rb.Push(RESULT_SUCCESS);
188 rb.PushIpcInterface(CreateProgressService(SyncType::Directory));
189 }
190
191 void SetPassphrase(Kernel::HLERequestContext& ctx) {
192 IPC::RequestParser rp{ctx};
193 const auto title_id = rp.PopRaw<u64>();
194
195 const auto passphrase_raw = ctx.ReadBuffer();
196
197 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, passphrase={}", title_id,
198 Common::HexToString(passphrase_raw));
199
200 if (title_id == 0) {
201 LOG_ERROR(Service_BCAT, "Invalid title ID!");
202 IPC::ResponseBuilder rb{ctx, 2};
203 rb.Push(ERROR_INVALID_ARGUMENT);
204 }
205
206 if (passphrase_raw.size() > 0x40) {
207 LOG_ERROR(Service_BCAT, "Passphrase too large!");
208 IPC::ResponseBuilder rb{ctx, 2};
209 rb.Push(ERROR_INVALID_ARGUMENT);
210 return;
211 }
212
213 Passphrase passphrase{};
214 std::memcpy(passphrase.data(), passphrase_raw.data(),
215 std::min(passphrase.size(), passphrase_raw.size()));
216
217 backend.SetPassphrase(title_id, passphrase);
218
219 IPC::ResponseBuilder rb{ctx, 2};
220 rb.Push(RESULT_SUCCESS);
221 }
222
223 void ClearDeliveryCacheStorage(Kernel::HLERequestContext& ctx) {
224 IPC::RequestParser rp{ctx};
225 const auto title_id = rp.PopRaw<u64>();
226
227 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", title_id);
228
229 if (title_id == 0) {
230 LOG_ERROR(Service_BCAT, "Invalid title ID!");
231 IPC::ResponseBuilder rb{ctx, 2};
232 rb.Push(ERROR_INVALID_ARGUMENT);
233 return;
234 }
235
236 if (!backend.Clear(title_id)) {
237 LOG_ERROR(Service_BCAT, "Could not clear the directory successfully!");
238 IPC::ResponseBuilder rb{ctx, 2};
239 rb.Push(ERROR_FAILED_CLEAR_CACHE);
240 return;
241 }
242
243 IPC::ResponseBuilder rb{ctx, 2};
244 rb.Push(RESULT_SUCCESS);
245 }
246
247 Backend& backend;
248
249 std::array<ProgressServiceBackend, static_cast<std::size_t>(SyncType::Count)> progress{
250 ProgressServiceBackend{"Normal"},
251 ProgressServiceBackend{"Directory"},
252 };
33}; 253};
34 254
35void Module::Interface::CreateBcatService(Kernel::HLERequestContext& ctx) { 255void Module::Interface::CreateBcatService(Kernel::HLERequestContext& ctx) {
@@ -37,20 +257,331 @@ void Module::Interface::CreateBcatService(Kernel::HLERequestContext& ctx) {
37 257
38 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 258 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
39 rb.Push(RESULT_SUCCESS); 259 rb.Push(RESULT_SUCCESS);
40 rb.PushIpcInterface<IBcatService>(); 260 rb.PushIpcInterface<IBcatService>(*backend);
261}
262
263class IDeliveryCacheFileService final : public ServiceFramework<IDeliveryCacheFileService> {
264public:
265 IDeliveryCacheFileService(FileSys::VirtualDir root_)
266 : ServiceFramework{"IDeliveryCacheFileService"}, root(std::move(root_)) {
267 // clang-format off
268 static const FunctionInfo functions[] = {
269 {0, &IDeliveryCacheFileService::Open, "Open"},
270 {1, &IDeliveryCacheFileService::Read, "Read"},
271 {2, &IDeliveryCacheFileService::GetSize, "GetSize"},
272 {3, &IDeliveryCacheFileService::GetDigest, "GetDigest"},
273 };
274 // clang-format on
275
276 RegisterHandlers(functions);
277 }
278
279private:
280 void Open(Kernel::HLERequestContext& ctx) {
281 IPC::RequestParser rp{ctx};
282 const auto dir_name_raw = rp.PopRaw<DirectoryName>();
283 const auto file_name_raw = rp.PopRaw<FileName>();
284
285 const auto dir_name =
286 Common::StringFromFixedZeroTerminatedBuffer(dir_name_raw.data(), dir_name_raw.size());
287 const auto file_name =
288 Common::StringFromFixedZeroTerminatedBuffer(file_name_raw.data(), file_name_raw.size());
289
290 LOG_DEBUG(Service_BCAT, "called, dir_name={}, file_name={}", dir_name, file_name);
291
292 if (!VerifyNameValidDir(ctx, dir_name_raw) || !VerifyNameValidFile(ctx, file_name_raw))
293 return;
294
295 if (current_file != nullptr) {
296 LOG_ERROR(Service_BCAT, "A file has already been opened on this interface!");
297 IPC::ResponseBuilder rb{ctx, 2};
298 rb.Push(ERROR_ENTITY_ALREADY_OPEN);
299 return;
300 }
301
302 const auto dir = root->GetSubdirectory(dir_name);
303
304 if (dir == nullptr) {
305 LOG_ERROR(Service_BCAT, "The directory of name={} couldn't be opened!", dir_name);
306 IPC::ResponseBuilder rb{ctx, 2};
307 rb.Push(ERROR_FAILED_OPEN_ENTITY);
308 return;
309 }
310
311 current_file = dir->GetFile(file_name);
312
313 if (current_file == nullptr) {
314 LOG_ERROR(Service_BCAT, "The file of name={} couldn't be opened!", file_name);
315 IPC::ResponseBuilder rb{ctx, 2};
316 rb.Push(ERROR_FAILED_OPEN_ENTITY);
317 return;
318 }
319
320 IPC::ResponseBuilder rb{ctx, 2};
321 rb.Push(RESULT_SUCCESS);
322 }
323
324 void Read(Kernel::HLERequestContext& ctx) {
325 IPC::RequestParser rp{ctx};
326 const auto offset{rp.PopRaw<u64>()};
327
328 auto size = ctx.GetWriteBufferSize();
329
330 LOG_DEBUG(Service_BCAT, "called, offset={:016X}, size={:016X}", offset, size);
331
332 if (current_file == nullptr) {
333 LOG_ERROR(Service_BCAT, "There is no file currently open!");
334 IPC::ResponseBuilder rb{ctx, 2};
335 rb.Push(ERROR_NO_OPEN_ENTITY);
336 }
337
338 size = std::min<u64>(current_file->GetSize() - offset, size);
339 const auto buffer = current_file->ReadBytes(size, offset);
340 ctx.WriteBuffer(buffer);
341
342 IPC::ResponseBuilder rb{ctx, 4};
343 rb.Push(RESULT_SUCCESS);
344 rb.Push<u64>(buffer.size());
345 }
346
347 void GetSize(Kernel::HLERequestContext& ctx) {
348 LOG_DEBUG(Service_BCAT, "called");
349
350 if (current_file == nullptr) {
351 LOG_ERROR(Service_BCAT, "There is no file currently open!");
352 IPC::ResponseBuilder rb{ctx, 2};
353 rb.Push(ERROR_NO_OPEN_ENTITY);
354 }
355
356 IPC::ResponseBuilder rb{ctx, 4};
357 rb.Push(RESULT_SUCCESS);
358 rb.Push<u64>(current_file->GetSize());
359 }
360
361 void GetDigest(Kernel::HLERequestContext& ctx) {
362 LOG_DEBUG(Service_BCAT, "called");
363
364 if (current_file == nullptr) {
365 LOG_ERROR(Service_BCAT, "There is no file currently open!");
366 IPC::ResponseBuilder rb{ctx, 2};
367 rb.Push(ERROR_NO_OPEN_ENTITY);
368 }
369
370 IPC::ResponseBuilder rb{ctx, 6};
371 rb.Push(RESULT_SUCCESS);
372 rb.PushRaw(DigestFile(current_file));
373 }
374
375 FileSys::VirtualDir root;
376 FileSys::VirtualFile current_file;
377};
378
379class IDeliveryCacheDirectoryService final
380 : public ServiceFramework<IDeliveryCacheDirectoryService> {
381public:
382 IDeliveryCacheDirectoryService(FileSys::VirtualDir root_)
383 : ServiceFramework{"IDeliveryCacheDirectoryService"}, root(std::move(root_)) {
384 // clang-format off
385 static const FunctionInfo functions[] = {
386 {0, &IDeliveryCacheDirectoryService::Open, "Open"},
387 {1, &IDeliveryCacheDirectoryService::Read, "Read"},
388 {2, &IDeliveryCacheDirectoryService::GetCount, "GetCount"},
389 };
390 // clang-format on
391
392 RegisterHandlers(functions);
393 }
394
395private:
396 void Open(Kernel::HLERequestContext& ctx) {
397 IPC::RequestParser rp{ctx};
398 const auto name_raw = rp.PopRaw<DirectoryName>();
399 const auto name =
400 Common::StringFromFixedZeroTerminatedBuffer(name_raw.data(), name_raw.size());
401
402 LOG_DEBUG(Service_BCAT, "called, name={}", name);
403
404 if (!VerifyNameValidDir(ctx, name_raw))
405 return;
406
407 if (current_dir != nullptr) {
408 LOG_ERROR(Service_BCAT, "A file has already been opened on this interface!");
409 IPC::ResponseBuilder rb{ctx, 2};
410 rb.Push(ERROR_ENTITY_ALREADY_OPEN);
411 return;
412 }
413
414 current_dir = root->GetSubdirectory(name);
415
416 if (current_dir == nullptr) {
417 LOG_ERROR(Service_BCAT, "Failed to open the directory name={}!", name);
418 IPC::ResponseBuilder rb{ctx, 2};
419 rb.Push(ERROR_FAILED_OPEN_ENTITY);
420 return;
421 }
422
423 IPC::ResponseBuilder rb{ctx, 2};
424 rb.Push(RESULT_SUCCESS);
425 }
426
427 void Read(Kernel::HLERequestContext& ctx) {
428 auto write_size = ctx.GetWriteBufferSize() / sizeof(DeliveryCacheDirectoryEntry);
429
430 LOG_DEBUG(Service_BCAT, "called, write_size={:016X}", write_size);
431
432 if (current_dir == nullptr) {
433 LOG_ERROR(Service_BCAT, "There is no open directory!");
434 IPC::ResponseBuilder rb{ctx, 2};
435 rb.Push(ERROR_NO_OPEN_ENTITY);
436 return;
437 }
438
439 const auto files = current_dir->GetFiles();
440 write_size = std::min<u64>(write_size, files.size());
441 std::vector<DeliveryCacheDirectoryEntry> entries(write_size);
442 std::transform(
443 files.begin(), files.begin() + write_size, entries.begin(), [](const auto& file) {
444 FileName name{};
445 std::memcpy(name.data(), file->GetName().data(),
446 std::min(file->GetName().size(), name.size()));
447 return DeliveryCacheDirectoryEntry{name, file->GetSize(), DigestFile(file)};
448 });
449
450 ctx.WriteBuffer(entries);
451
452 IPC::ResponseBuilder rb{ctx, 3};
453 rb.Push(RESULT_SUCCESS);
454 rb.Push<u32>(write_size * sizeof(DeliveryCacheDirectoryEntry));
455 }
456
457 void GetCount(Kernel::HLERequestContext& ctx) {
458 LOG_DEBUG(Service_BCAT, "called");
459
460 if (current_dir == nullptr) {
461 LOG_ERROR(Service_BCAT, "There is no open directory!");
462 IPC::ResponseBuilder rb{ctx, 2};
463 rb.Push(ERROR_NO_OPEN_ENTITY);
464 return;
465 }
466
467 const auto files = current_dir->GetFiles();
468
469 IPC::ResponseBuilder rb{ctx, 3};
470 rb.Push(RESULT_SUCCESS);
471 rb.Push<u32>(files.size());
472 }
473
474 FileSys::VirtualDir root;
475 FileSys::VirtualDir current_dir;
476};
477
478class IDeliveryCacheStorageService final : public ServiceFramework<IDeliveryCacheStorageService> {
479public:
480 IDeliveryCacheStorageService(FileSys::VirtualDir root_)
481 : ServiceFramework{"IDeliveryCacheStorageService"}, root(std::move(root_)) {
482 // clang-format off
483 static const FunctionInfo functions[] = {
484 {0, &IDeliveryCacheStorageService::CreateFileService, "CreateFileService"},
485 {1, &IDeliveryCacheStorageService::CreateDirectoryService, "CreateDirectoryService"},
486 {10, &IDeliveryCacheStorageService::EnumerateDeliveryCacheDirectory, "EnumerateDeliveryCacheDirectory"},
487 };
488 // clang-format on
489
490 RegisterHandlers(functions);
491
492 for (const auto& subdir : root->GetSubdirectories()) {
493 DirectoryName name{};
494 std::memcpy(name.data(), subdir->GetName().data(),
495 std::min(sizeof(DirectoryName) - 1, subdir->GetName().size()));
496 entries.push_back(name);
497 }
498 }
499
500private:
501 void CreateFileService(Kernel::HLERequestContext& ctx) {
502 LOG_DEBUG(Service_BCAT, "called");
503
504 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
505 rb.Push(RESULT_SUCCESS);
506 rb.PushIpcInterface<IDeliveryCacheFileService>(root);
507 }
508
509 void CreateDirectoryService(Kernel::HLERequestContext& ctx) {
510 LOG_DEBUG(Service_BCAT, "called");
511
512 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
513 rb.Push(RESULT_SUCCESS);
514 rb.PushIpcInterface<IDeliveryCacheDirectoryService>(root);
515 }
516
517 void EnumerateDeliveryCacheDirectory(Kernel::HLERequestContext& ctx) {
518 auto size = ctx.GetWriteBufferSize() / sizeof(DirectoryName);
519
520 LOG_DEBUG(Service_BCAT, "called, size={:016X}", size);
521
522 size = std::min<u64>(size, entries.size() - next_read_index);
523 ctx.WriteBuffer(entries.data() + next_read_index, size * sizeof(DirectoryName));
524 next_read_index += size;
525
526 IPC::ResponseBuilder rb{ctx, 3};
527 rb.Push(RESULT_SUCCESS);
528 rb.Push<u32>(size);
529 }
530
531 FileSys::VirtualDir root;
532 std::vector<DirectoryName> entries;
533 u64 next_read_index = 0;
534};
535
536void Module::Interface::CreateDeliveryCacheStorageService(Kernel::HLERequestContext& ctx) {
537 LOG_DEBUG(Service_BCAT, "called");
538
539 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
540 rb.Push(RESULT_SUCCESS);
541 rb.PushIpcInterface<IDeliveryCacheStorageService>(
542 fsc.GetBCATDirectory(Core::CurrentProcess()->GetTitleID()));
543}
544
545void Module::Interface::CreateDeliveryCacheStorageServiceWithApplicationId(
546 Kernel::HLERequestContext& ctx) {
547 IPC::RequestParser rp{ctx};
548 const auto title_id = rp.PopRaw<u64>();
549
550 LOG_DEBUG(Service_BCAT, "called, title_id={:016X}", title_id);
551
552 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
553 rb.Push(RESULT_SUCCESS);
554 rb.PushIpcInterface<IDeliveryCacheStorageService>(fsc.GetBCATDirectory(title_id));
555}
556
557std::unique_ptr<Backend> CreateBackendFromSettings(DirectoryGetter getter) {
558 const auto backend = Settings::values.bcat_backend;
559
560#ifdef YUZU_ENABLE_BOXCAT
561 if (backend == "boxcat")
562 return std::make_unique<Boxcat>(std::move(getter));
563#endif
564
565 return std::make_unique<NullBackend>(std::move(getter));
41} 566}
42 567
43Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) 568Module::Interface::Interface(std::shared_ptr<Module> module, FileSystem::FileSystemController& fsc,
44 : ServiceFramework(name), module(std::move(module)) {} 569 const char* name)
570 : ServiceFramework(name), module(std::move(module)), fsc(fsc),
571 backend(CreateBackendFromSettings([&fsc](u64 tid) { return fsc.GetBCATDirectory(tid); })) {}
45 572
46Module::Interface::~Interface() = default; 573Module::Interface::~Interface() = default;
47 574
48void InstallInterfaces(SM::ServiceManager& service_manager) { 575void InstallInterfaces(Core::System& system) {
49 auto module = std::make_shared<Module>(); 576 auto module = std::make_shared<Module>();
50 std::make_shared<BCAT>(module, "bcat:a")->InstallAsService(service_manager); 577 std::make_shared<BCAT>(module, system.GetFileSystemController(), "bcat:a")
51 std::make_shared<BCAT>(module, "bcat:m")->InstallAsService(service_manager); 578 ->InstallAsService(system.ServiceManager());
52 std::make_shared<BCAT>(module, "bcat:u")->InstallAsService(service_manager); 579 std::make_shared<BCAT>(module, system.GetFileSystemController(), "bcat:m")
53 std::make_shared<BCAT>(module, "bcat:s")->InstallAsService(service_manager); 580 ->InstallAsService(system.ServiceManager());
581 std::make_shared<BCAT>(module, system.GetFileSystemController(), "bcat:u")
582 ->InstallAsService(system.ServiceManager());
583 std::make_shared<BCAT>(module, system.GetFileSystemController(), "bcat:s")
584 ->InstallAsService(system.ServiceManager());
54} 585}
55 586
56} // namespace Service::BCAT 587} // namespace Service::BCAT
diff --git a/src/core/hle/service/bcat/module.h b/src/core/hle/service/bcat/module.h
index f0d63cab0..27469926a 100644
--- a/src/core/hle/service/bcat/module.h
+++ b/src/core/hle/service/bcat/module.h
@@ -6,23 +6,39 @@
6 6
7#include "core/hle/service/service.h" 7#include "core/hle/service/service.h"
8 8
9namespace Service::BCAT { 9namespace Service {
10
11namespace FileSystem {
12class FileSystemController;
13} // namespace FileSystem
14
15namespace BCAT {
16
17class Backend;
10 18
11class Module final { 19class Module final {
12public: 20public:
13 class Interface : public ServiceFramework<Interface> { 21 class Interface : public ServiceFramework<Interface> {
14 public: 22 public:
15 explicit Interface(std::shared_ptr<Module> module, const char* name); 23 explicit Interface(std::shared_ptr<Module> module, FileSystem::FileSystemController& fsc,
24 const char* name);
16 ~Interface() override; 25 ~Interface() override;
17 26
18 void CreateBcatService(Kernel::HLERequestContext& ctx); 27 void CreateBcatService(Kernel::HLERequestContext& ctx);
28 void CreateDeliveryCacheStorageService(Kernel::HLERequestContext& ctx);
29 void CreateDeliveryCacheStorageServiceWithApplicationId(Kernel::HLERequestContext& ctx);
19 30
20 protected: 31 protected:
32 FileSystem::FileSystemController& fsc;
33
21 std::shared_ptr<Module> module; 34 std::shared_ptr<Module> module;
35 std::unique_ptr<Backend> backend;
22 }; 36 };
23}; 37};
24 38
25/// Registers all BCAT services with the specified service manager. 39/// Registers all BCAT services with the specified service manager.
26void InstallInterfaces(SM::ServiceManager& service_manager); 40void InstallInterfaces(Core::System& system);
41
42} // namespace BCAT
27 43
28} // namespace Service::BCAT 44} // namespace Service
diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp
index 3c7ca2c44..afce581e5 100644
--- a/src/core/hle/service/btdrv/btdrv.cpp
+++ b/src/core/hle/service/btdrv/btdrv.cpp
@@ -16,7 +16,7 @@ namespace Service::BtDrv {
16 16
17class Bt final : public ServiceFramework<Bt> { 17class Bt final : public ServiceFramework<Bt> {
18public: 18public:
19 explicit Bt() : ServiceFramework{"bt"} { 19 explicit Bt(Core::System& system) : ServiceFramework{"bt"} {
20 // clang-format off 20 // clang-format off
21 static const FunctionInfo functions[] = { 21 static const FunctionInfo functions[] = {
22 {0, nullptr, "LeClientReadCharacteristic"}, 22 {0, nullptr, "LeClientReadCharacteristic"},
@@ -33,7 +33,7 @@ public:
33 // clang-format on 33 // clang-format on
34 RegisterHandlers(functions); 34 RegisterHandlers(functions);
35 35
36 auto& kernel = Core::System::GetInstance().Kernel(); 36 auto& kernel = system.Kernel();
37 register_event = Kernel::WritableEvent::CreateEventPair( 37 register_event = Kernel::WritableEvent::CreateEventPair(
38 kernel, Kernel::ResetType::Automatic, "BT:RegisterEvent"); 38 kernel, Kernel::ResetType::Automatic, "BT:RegisterEvent");
39 } 39 }
@@ -163,9 +163,9 @@ public:
163 } 163 }
164}; 164};
165 165
166void InstallInterfaces(SM::ServiceManager& sm) { 166void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
167 std::make_shared<BtDrv>()->InstallAsService(sm); 167 std::make_shared<BtDrv>()->InstallAsService(sm);
168 std::make_shared<Bt>()->InstallAsService(sm); 168 std::make_shared<Bt>(system)->InstallAsService(sm);
169} 169}
170 170
171} // namespace Service::BtDrv 171} // namespace Service::BtDrv
diff --git a/src/core/hle/service/btdrv/btdrv.h b/src/core/hle/service/btdrv/btdrv.h
index 164e56f43..191410dbc 100644
--- a/src/core/hle/service/btdrv/btdrv.h
+++ b/src/core/hle/service/btdrv/btdrv.h
@@ -8,9 +8,13 @@ namespace Service::SM {
8class ServiceManager; 8class ServiceManager;
9} 9}
10 10
11namespace Core {
12class System;
13}
14
11namespace Service::BtDrv { 15namespace Service::BtDrv {
12 16
13/// Registers all BtDrv services with the specified service manager. 17/// Registers all BtDrv services with the specified service manager.
14void InstallInterfaces(SM::ServiceManager& sm); 18void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
15 19
16} // namespace Service::BtDrv 20} // namespace Service::BtDrv
diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp
index b439ee7ec..920fc6ff7 100644
--- a/src/core/hle/service/btm/btm.cpp
+++ b/src/core/hle/service/btm/btm.cpp
@@ -17,7 +17,7 @@ namespace Service::BTM {
17 17
18class IBtmUserCore final : public ServiceFramework<IBtmUserCore> { 18class IBtmUserCore final : public ServiceFramework<IBtmUserCore> {
19public: 19public:
20 explicit IBtmUserCore() : ServiceFramework{"IBtmUserCore"} { 20 explicit IBtmUserCore(Core::System& system) : ServiceFramework{"IBtmUserCore"} {
21 // clang-format off 21 // clang-format off
22 static const FunctionInfo functions[] = { 22 static const FunctionInfo functions[] = {
23 {0, &IBtmUserCore::AcquireBleScanEvent, "AcquireBleScanEvent"}, 23 {0, &IBtmUserCore::AcquireBleScanEvent, "AcquireBleScanEvent"},
@@ -56,7 +56,7 @@ public:
56 // clang-format on 56 // clang-format on
57 RegisterHandlers(functions); 57 RegisterHandlers(functions);
58 58
59 auto& kernel = Core::System::GetInstance().Kernel(); 59 auto& kernel = system.Kernel();
60 scan_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic, 60 scan_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
61 "IBtmUserCore:ScanEvent"); 61 "IBtmUserCore:ScanEvent");
62 connection_event = Kernel::WritableEvent::CreateEventPair( 62 connection_event = Kernel::WritableEvent::CreateEventPair(
@@ -108,7 +108,7 @@ private:
108 108
109class BTM_USR final : public ServiceFramework<BTM_USR> { 109class BTM_USR final : public ServiceFramework<BTM_USR> {
110public: 110public:
111 explicit BTM_USR() : ServiceFramework{"btm:u"} { 111 explicit BTM_USR(Core::System& system) : ServiceFramework{"btm:u"}, system(system) {
112 // clang-format off 112 // clang-format off
113 static const FunctionInfo functions[] = { 113 static const FunctionInfo functions[] = {
114 {0, &BTM_USR::GetCore, "GetCore"}, 114 {0, &BTM_USR::GetCore, "GetCore"},
@@ -123,8 +123,10 @@ private:
123 123
124 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 124 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
125 rb.Push(RESULT_SUCCESS); 125 rb.Push(RESULT_SUCCESS);
126 rb.PushIpcInterface<IBtmUserCore>(); 126 rb.PushIpcInterface<IBtmUserCore>(system);
127 } 127 }
128
129 Core::System& system;
128}; 130};
129 131
130class BTM final : public ServiceFramework<BTM> { 132class BTM final : public ServiceFramework<BTM> {
@@ -268,11 +270,11 @@ private:
268 } 270 }
269}; 271};
270 272
271void InstallInterfaces(SM::ServiceManager& sm) { 273void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
272 std::make_shared<BTM>()->InstallAsService(sm); 274 std::make_shared<BTM>()->InstallAsService(sm);
273 std::make_shared<BTM_DBG>()->InstallAsService(sm); 275 std::make_shared<BTM_DBG>()->InstallAsService(sm);
274 std::make_shared<BTM_SYS>()->InstallAsService(sm); 276 std::make_shared<BTM_SYS>()->InstallAsService(sm);
275 std::make_shared<BTM_USR>()->InstallAsService(sm); 277 std::make_shared<BTM_USR>(system)->InstallAsService(sm);
276} 278}
277 279
278} // namespace Service::BTM 280} // namespace Service::BTM
diff --git a/src/core/hle/service/btm/btm.h b/src/core/hle/service/btm/btm.h
index e6425a7e3..c6b878043 100644
--- a/src/core/hle/service/btm/btm.h
+++ b/src/core/hle/service/btm/btm.h
@@ -8,8 +8,12 @@ namespace Service::SM {
8class ServiceManager; 8class ServiceManager;
9} 9}
10 10
11namespace Core {
12class System;
13};
14
11namespace Service::BTM { 15namespace Service::BTM {
12 16
13void InstallInterfaces(SM::ServiceManager& sm); 17void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
14 18
15} // namespace Service::BTM 19} // namespace Service::BTM
diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp
index 01fa06ad3..b2ebf6240 100644
--- a/src/core/hle/service/fatal/fatal.cpp
+++ b/src/core/hle/service/fatal/fatal.cpp
@@ -20,8 +20,8 @@
20 20
21namespace Service::Fatal { 21namespace Service::Fatal {
22 22
23Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) 23Module::Interface::Interface(std::shared_ptr<Module> module, Core::System& system, const char* name)
24 : ServiceFramework(name), module(std::move(module)) {} 24 : ServiceFramework(name), module(std::move(module)), system(system) {}
25 25
26Module::Interface::~Interface() = default; 26Module::Interface::~Interface() = default;
27 27
@@ -64,7 +64,8 @@ enum class FatalType : u32 {
64 ErrorScreen = 2, 64 ErrorScreen = 2,
65}; 65};
66 66
67static void GenerateErrorReport(ResultCode error_code, const FatalInfo& info) { 67static void GenerateErrorReport(Core::System& system, ResultCode error_code,
68 const FatalInfo& info) {
68 const auto title_id = Core::CurrentProcess()->GetTitleID(); 69 const auto title_id = Core::CurrentProcess()->GetTitleID();
69 std::string crash_report = fmt::format( 70 std::string crash_report = fmt::format(
70 "Yuzu {}-{} crash report\n" 71 "Yuzu {}-{} crash report\n"
@@ -101,18 +102,19 @@ static void GenerateErrorReport(ResultCode error_code, const FatalInfo& info) {
101 102
102 LOG_ERROR(Service_Fatal, "{}", crash_report); 103 LOG_ERROR(Service_Fatal, "{}", crash_report);
103 104
104 Core::System::GetInstance().GetReporter().SaveCrashReport( 105 system.GetReporter().SaveCrashReport(
105 title_id, error_code, info.set_flags, info.program_entry_point, info.sp, info.pc, 106 title_id, error_code, info.set_flags, info.program_entry_point, info.sp, info.pc,
106 info.pstate, info.afsr0, info.afsr1, info.esr, info.far, info.registers, info.backtrace, 107 info.pstate, info.afsr0, info.afsr1, info.esr, info.far, info.registers, info.backtrace,
107 info.backtrace_size, info.ArchAsString(), info.unk10); 108 info.backtrace_size, info.ArchAsString(), info.unk10);
108} 109}
109 110
110static void ThrowFatalError(ResultCode error_code, FatalType fatal_type, const FatalInfo& info) { 111static void ThrowFatalError(Core::System& system, ResultCode error_code, FatalType fatal_type,
112 const FatalInfo& info) {
111 LOG_ERROR(Service_Fatal, "Threw fatal error type {} with error code 0x{:X}", 113 LOG_ERROR(Service_Fatal, "Threw fatal error type {} with error code 0x{:X}",
112 static_cast<u32>(fatal_type), error_code.raw); 114 static_cast<u32>(fatal_type), error_code.raw);
113 switch (fatal_type) { 115 switch (fatal_type) {
114 case FatalType::ErrorReportAndScreen: 116 case FatalType::ErrorReportAndScreen:
115 GenerateErrorReport(error_code, info); 117 GenerateErrorReport(system, error_code, info);
116 [[fallthrough]]; 118 [[fallthrough]];
117 case FatalType::ErrorScreen: 119 case FatalType::ErrorScreen:
118 // Since we have no fatal:u error screen. We should just kill execution instead 120 // Since we have no fatal:u error screen. We should just kill execution instead
@@ -120,7 +122,7 @@ static void ThrowFatalError(ResultCode error_code, FatalType fatal_type, const F
120 break; 122 break;
121 // Should not throw a fatal screen but should generate an error report 123 // Should not throw a fatal screen but should generate an error report
122 case FatalType::ErrorReport: 124 case FatalType::ErrorReport:
123 GenerateErrorReport(error_code, info); 125 GenerateErrorReport(system, error_code, info);
124 break; 126 break;
125 } 127 }
126} 128}
@@ -130,7 +132,7 @@ void Module::Interface::ThrowFatal(Kernel::HLERequestContext& ctx) {
130 IPC::RequestParser rp{ctx}; 132 IPC::RequestParser rp{ctx};
131 const auto error_code = rp.Pop<ResultCode>(); 133 const auto error_code = rp.Pop<ResultCode>();
132 134
133 ThrowFatalError(error_code, FatalType::ErrorScreen, {}); 135 ThrowFatalError(system, error_code, FatalType::ErrorScreen, {});
134 IPC::ResponseBuilder rb{ctx, 2}; 136 IPC::ResponseBuilder rb{ctx, 2};
135 rb.Push(RESULT_SUCCESS); 137 rb.Push(RESULT_SUCCESS);
136} 138}
@@ -141,7 +143,8 @@ void Module::Interface::ThrowFatalWithPolicy(Kernel::HLERequestContext& ctx) {
141 const auto error_code = rp.Pop<ResultCode>(); 143 const auto error_code = rp.Pop<ResultCode>();
142 const auto fatal_type = rp.PopEnum<FatalType>(); 144 const auto fatal_type = rp.PopEnum<FatalType>();
143 145
144 ThrowFatalError(error_code, fatal_type, {}); // No info is passed with ThrowFatalWithPolicy 146 ThrowFatalError(system, error_code, fatal_type,
147 {}); // No info is passed with ThrowFatalWithPolicy
145 IPC::ResponseBuilder rb{ctx, 2}; 148 IPC::ResponseBuilder rb{ctx, 2};
146 rb.Push(RESULT_SUCCESS); 149 rb.Push(RESULT_SUCCESS);
147} 150}
@@ -157,15 +160,15 @@ void Module::Interface::ThrowFatalWithCpuContext(Kernel::HLERequestContext& ctx)
157 ASSERT_MSG(fatal_info.size() == sizeof(FatalInfo), "Invalid fatal info buffer size!"); 160 ASSERT_MSG(fatal_info.size() == sizeof(FatalInfo), "Invalid fatal info buffer size!");
158 std::memcpy(&info, fatal_info.data(), sizeof(FatalInfo)); 161 std::memcpy(&info, fatal_info.data(), sizeof(FatalInfo));
159 162
160 ThrowFatalError(error_code, fatal_type, info); 163 ThrowFatalError(system, error_code, fatal_type, info);
161 IPC::ResponseBuilder rb{ctx, 2}; 164 IPC::ResponseBuilder rb{ctx, 2};
162 rb.Push(RESULT_SUCCESS); 165 rb.Push(RESULT_SUCCESS);
163} 166}
164 167
165void InstallInterfaces(SM::ServiceManager& service_manager) { 168void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
166 auto module = std::make_shared<Module>(); 169 auto module = std::make_shared<Module>();
167 std::make_shared<Fatal_P>(module)->InstallAsService(service_manager); 170 std::make_shared<Fatal_P>(module, system)->InstallAsService(service_manager);
168 std::make_shared<Fatal_U>(module)->InstallAsService(service_manager); 171 std::make_shared<Fatal_U>(module, system)->InstallAsService(service_manager);
169} 172}
170 173
171} // namespace Service::Fatal 174} // namespace Service::Fatal
diff --git a/src/core/hle/service/fatal/fatal.h b/src/core/hle/service/fatal/fatal.h
index 09371ff7f..bd9339dfc 100644
--- a/src/core/hle/service/fatal/fatal.h
+++ b/src/core/hle/service/fatal/fatal.h
@@ -6,13 +6,17 @@
6 6
7#include "core/hle/service/service.h" 7#include "core/hle/service/service.h"
8 8
9namespace Core {
10class System;
11}
12
9namespace Service::Fatal { 13namespace Service::Fatal {
10 14
11class Module final { 15class Module final {
12public: 16public:
13 class Interface : public ServiceFramework<Interface> { 17 class Interface : public ServiceFramework<Interface> {
14 public: 18 public:
15 explicit Interface(std::shared_ptr<Module> module, const char* name); 19 explicit Interface(std::shared_ptr<Module> module, Core::System& system, const char* name);
16 ~Interface() override; 20 ~Interface() override;
17 21
18 void ThrowFatal(Kernel::HLERequestContext& ctx); 22 void ThrowFatal(Kernel::HLERequestContext& ctx);
@@ -21,9 +25,10 @@ public:
21 25
22 protected: 26 protected:
23 std::shared_ptr<Module> module; 27 std::shared_ptr<Module> module;
28 Core::System& system;
24 }; 29 };
25}; 30};
26 31
27void InstallInterfaces(SM::ServiceManager& service_manager); 32void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
28 33
29} // namespace Service::Fatal 34} // namespace Service::Fatal
diff --git a/src/core/hle/service/fatal/fatal_p.cpp b/src/core/hle/service/fatal/fatal_p.cpp
index 9e5f872ff..066ccf6b0 100644
--- a/src/core/hle/service/fatal/fatal_p.cpp
+++ b/src/core/hle/service/fatal/fatal_p.cpp
@@ -6,8 +6,8 @@
6 6
7namespace Service::Fatal { 7namespace Service::Fatal {
8 8
9Fatal_P::Fatal_P(std::shared_ptr<Module> module) 9Fatal_P::Fatal_P(std::shared_ptr<Module> module, Core::System& system)
10 : Module::Interface(std::move(module), "fatal:p") {} 10 : Module::Interface(std::move(module), system, "fatal:p") {}
11 11
12Fatal_P::~Fatal_P() = default; 12Fatal_P::~Fatal_P() = default;
13 13
diff --git a/src/core/hle/service/fatal/fatal_p.h b/src/core/hle/service/fatal/fatal_p.h
index 6e9c5979f..c6d953cb5 100644
--- a/src/core/hle/service/fatal/fatal_p.h
+++ b/src/core/hle/service/fatal/fatal_p.h
@@ -10,7 +10,7 @@ namespace Service::Fatal {
10 10
11class Fatal_P final : public Module::Interface { 11class Fatal_P final : public Module::Interface {
12public: 12public:
13 explicit Fatal_P(std::shared_ptr<Module> module); 13 explicit Fatal_P(std::shared_ptr<Module> module, Core::System& system);
14 ~Fatal_P() override; 14 ~Fatal_P() override;
15}; 15};
16 16
diff --git a/src/core/hle/service/fatal/fatal_u.cpp b/src/core/hle/service/fatal/fatal_u.cpp
index 1572a2051..8d72ed485 100644
--- a/src/core/hle/service/fatal/fatal_u.cpp
+++ b/src/core/hle/service/fatal/fatal_u.cpp
@@ -6,7 +6,8 @@
6 6
7namespace Service::Fatal { 7namespace 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, Core::System& system)
10 : Module::Interface(std::move(module), system, "fatal:u") {
10 static const FunctionInfo functions[] = { 11 static const FunctionInfo functions[] = {
11 {0, &Fatal_U::ThrowFatal, "ThrowFatal"}, 12 {0, &Fatal_U::ThrowFatal, "ThrowFatal"},
12 {1, &Fatal_U::ThrowFatalWithPolicy, "ThrowFatalWithPolicy"}, 13 {1, &Fatal_U::ThrowFatalWithPolicy, "ThrowFatalWithPolicy"},
diff --git a/src/core/hle/service/fatal/fatal_u.h b/src/core/hle/service/fatal/fatal_u.h
index 72cb6d076..34c5c7f95 100644
--- a/src/core/hle/service/fatal/fatal_u.h
+++ b/src/core/hle/service/fatal/fatal_u.h
@@ -10,7 +10,7 @@ namespace Service::Fatal {
10 10
11class Fatal_U final : public Module::Interface { 11class Fatal_U final : public Module::Interface {
12public: 12public:
13 explicit Fatal_U(std::shared_ptr<Module> module); 13 explicit Fatal_U(std::shared_ptr<Module> module, Core::System& system);
14 ~Fatal_U() override; 14 ~Fatal_U() override;
15}; 15};
16 16
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 14cd0e322..7fa4e820b 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -674,6 +674,15 @@ FileSys::VirtualDir FileSystemController::GetModificationDumpRoot(u64 title_id)
674 return bis_factory->GetModificationDumpRoot(title_id); 674 return bis_factory->GetModificationDumpRoot(title_id);
675} 675}
676 676
677FileSys::VirtualDir FileSystemController::GetBCATDirectory(u64 title_id) const {
678 LOG_TRACE(Service_FS, "Opening BCAT root for tid={:016X}", title_id);
679
680 if (bis_factory == nullptr)
681 return nullptr;
682
683 return bis_factory->GetBCATDirectory(title_id);
684}
685
677void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) { 686void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
678 if (overwrite) { 687 if (overwrite) {
679 bis_factory = nullptr; 688 bis_factory = nullptr;
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index 3e0c03ec0..e6b49d8a2 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -110,6 +110,8 @@ public:
110 FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) const; 110 FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) const;
111 FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) const; 111 FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) const;
112 112
113 FileSys::VirtualDir GetBCATDirectory(u64 title_id) const;
114
113 // Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function 115 // Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function
114 // above is called. 116 // above is called.
115 void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true); 117 void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true);
diff --git a/src/core/hle/service/friend/friend.cpp b/src/core/hle/service/friend/friend.cpp
index d1ec12ef9..42b4ee861 100644
--- a/src/core/hle/service/friend/friend.cpp
+++ b/src/core/hle/service/friend/friend.cpp
@@ -149,7 +149,8 @@ private:
149 149
150class INotificationService final : public ServiceFramework<INotificationService> { 150class INotificationService final : public ServiceFramework<INotificationService> {
151public: 151public:
152 INotificationService(Common::UUID uuid) : ServiceFramework("INotificationService"), uuid(uuid) { 152 INotificationService(Common::UUID uuid, Core::System& system)
153 : ServiceFramework("INotificationService"), uuid(uuid) {
153 // clang-format off 154 // clang-format off
154 static const FunctionInfo functions[] = { 155 static const FunctionInfo functions[] = {
155 {0, &INotificationService::GetEvent, "GetEvent"}, 156 {0, &INotificationService::GetEvent, "GetEvent"},
@@ -159,6 +160,9 @@ public:
159 // clang-format on 160 // clang-format on
160 161
161 RegisterHandlers(functions); 162 RegisterHandlers(functions);
163
164 notification_event = Kernel::WritableEvent::CreateEventPair(
165 system.Kernel(), Kernel::ResetType::Manual, "INotificationService:NotifyEvent");
162 } 166 }
163 167
164private: 168private:
@@ -167,13 +171,6 @@ private:
167 171
168 IPC::ResponseBuilder rb{ctx, 2, 1}; 172 IPC::ResponseBuilder rb{ctx, 2, 1};
169 rb.Push(RESULT_SUCCESS); 173 rb.Push(RESULT_SUCCESS);
170
171 if (!is_event_created) {
172 auto& kernel = Core::System::GetInstance().Kernel();
173 notification_event = Kernel::WritableEvent::CreateEventPair(
174 kernel, Kernel::ResetType::Manual, "INotificationService:NotifyEvent");
175 is_event_created = true;
176 }
177 rb.PushCopyObjects(notification_event.readable); 174 rb.PushCopyObjects(notification_event.readable);
178 } 175 }
179 176
@@ -261,21 +258,21 @@ void Module::Interface::CreateNotificationService(Kernel::HLERequestContext& ctx
261 258
262 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 259 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
263 rb.Push(RESULT_SUCCESS); 260 rb.Push(RESULT_SUCCESS);
264 rb.PushIpcInterface<INotificationService>(uuid); 261 rb.PushIpcInterface<INotificationService>(uuid, system);
265} 262}
266 263
267Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) 264Module::Interface::Interface(std::shared_ptr<Module> module, Core::System& system, const char* name)
268 : ServiceFramework(name), module(std::move(module)) {} 265 : ServiceFramework(name), module(std::move(module)), system(system) {}
269 266
270Module::Interface::~Interface() = default; 267Module::Interface::~Interface() = default;
271 268
272void InstallInterfaces(SM::ServiceManager& service_manager) { 269void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
273 auto module = std::make_shared<Module>(); 270 auto module = std::make_shared<Module>();
274 std::make_shared<Friend>(module, "friend:a")->InstallAsService(service_manager); 271 std::make_shared<Friend>(module, system, "friend:a")->InstallAsService(service_manager);
275 std::make_shared<Friend>(module, "friend:m")->InstallAsService(service_manager); 272 std::make_shared<Friend>(module, system, "friend:m")->InstallAsService(service_manager);
276 std::make_shared<Friend>(module, "friend:s")->InstallAsService(service_manager); 273 std::make_shared<Friend>(module, system, "friend:s")->InstallAsService(service_manager);
277 std::make_shared<Friend>(module, "friend:u")->InstallAsService(service_manager); 274 std::make_shared<Friend>(module, system, "friend:u")->InstallAsService(service_manager);
278 std::make_shared<Friend>(module, "friend:v")->InstallAsService(service_manager); 275 std::make_shared<Friend>(module, system, "friend:v")->InstallAsService(service_manager);
279} 276}
280 277
281} // namespace Service::Friend 278} // namespace Service::Friend
diff --git a/src/core/hle/service/friend/friend.h b/src/core/hle/service/friend/friend.h
index 38d05fa8e..24f3fc969 100644
--- a/src/core/hle/service/friend/friend.h
+++ b/src/core/hle/service/friend/friend.h
@@ -6,13 +6,17 @@
6 6
7#include "core/hle/service/service.h" 7#include "core/hle/service/service.h"
8 8
9namespace Core {
10class System;
11}
12
9namespace Service::Friend { 13namespace Service::Friend {
10 14
11class Module final { 15class Module final {
12public: 16public:
13 class Interface : public ServiceFramework<Interface> { 17 class Interface : public ServiceFramework<Interface> {
14 public: 18 public:
15 explicit Interface(std::shared_ptr<Module> module, const char* name); 19 explicit Interface(std::shared_ptr<Module> module, Core::System& system, const char* name);
16 ~Interface() override; 20 ~Interface() override;
17 21
18 void CreateFriendService(Kernel::HLERequestContext& ctx); 22 void CreateFriendService(Kernel::HLERequestContext& ctx);
@@ -20,10 +24,11 @@ public:
20 24
21 protected: 25 protected:
22 std::shared_ptr<Module> module; 26 std::shared_ptr<Module> module;
27 Core::System& system;
23 }; 28 };
24}; 29};
25 30
26/// Registers all Friend services with the specified service manager. 31/// Registers all Friend services with the specified service manager.
27void InstallInterfaces(SM::ServiceManager& service_manager); 32void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
28 33
29} // namespace Service::Friend 34} // namespace Service::Friend
diff --git a/src/core/hle/service/friend/interface.cpp b/src/core/hle/service/friend/interface.cpp
index 5b384f733..58155f652 100644
--- a/src/core/hle/service/friend/interface.cpp
+++ b/src/core/hle/service/friend/interface.cpp
@@ -6,8 +6,8 @@
6 6
7namespace Service::Friend { 7namespace Service::Friend {
8 8
9Friend::Friend(std::shared_ptr<Module> module, const char* name) 9Friend::Friend(std::shared_ptr<Module> module, Core::System& system, const char* name)
10 : Interface(std::move(module), name) { 10 : Interface(std::move(module), system, name) {
11 static const FunctionInfo functions[] = { 11 static const FunctionInfo functions[] = {
12 {0, &Friend::CreateFriendService, "CreateFriendService"}, 12 {0, &Friend::CreateFriendService, "CreateFriendService"},
13 {1, &Friend::CreateNotificationService, "CreateNotificationService"}, 13 {1, &Friend::CreateNotificationService, "CreateNotificationService"},
diff --git a/src/core/hle/service/friend/interface.h b/src/core/hle/service/friend/interface.h
index 1963def39..465a35770 100644
--- a/src/core/hle/service/friend/interface.h
+++ b/src/core/hle/service/friend/interface.h
@@ -10,7 +10,7 @@ namespace Service::Friend {
10 10
11class Friend final : public Module::Interface { 11class Friend final : public Module::Interface {
12public: 12public:
13 explicit Friend(std::shared_ptr<Module> module, const char* name); 13 explicit Friend(std::shared_ptr<Module> module, Core::System& system, const char* name);
14 ~Friend() override; 14 ~Friend() override;
15}; 15};
16 16
diff --git a/src/core/hle/service/hid/controllers/controller_base.cpp b/src/core/hle/service/hid/controllers/controller_base.cpp
index 0993a7815..8091db9d7 100644
--- a/src/core/hle/service/hid/controllers/controller_base.cpp
+++ b/src/core/hle/service/hid/controllers/controller_base.cpp
@@ -6,7 +6,7 @@
6 6
7namespace Service::HID { 7namespace Service::HID {
8 8
9ControllerBase::ControllerBase() = default; 9ControllerBase::ControllerBase(Core::System& system) : system(system) {}
10ControllerBase::~ControllerBase() = default; 10ControllerBase::~ControllerBase() = default;
11 11
12void ControllerBase::ActivateController() { 12void ControllerBase::ActivateController() {
diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h
index 5e5097a03..8bc69c372 100644
--- a/src/core/hle/service/hid/controllers/controller_base.h
+++ b/src/core/hle/service/hid/controllers/controller_base.h
@@ -11,10 +11,14 @@ namespace Core::Timing {
11class CoreTiming; 11class CoreTiming;
12} 12}
13 13
14namespace Core {
15class System;
16}
17
14namespace Service::HID { 18namespace Service::HID {
15class ControllerBase { 19class ControllerBase {
16public: 20public:
17 ControllerBase(); 21 explicit ControllerBase(Core::System& system);
18 virtual ~ControllerBase(); 22 virtual ~ControllerBase();
19 23
20 // Called when the controller is initialized 24 // Called when the controller is initialized
@@ -46,5 +50,7 @@ protected:
46 s64_le entry_count; 50 s64_le entry_count;
47 }; 51 };
48 static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size"); 52 static_assert(sizeof(CommonHeader) == 0x20, "CommonHeader is an invalid size");
53
54 Core::System& system;
49}; 55};
50} // namespace Service::HID 56} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp
index c5c2e032a..8e8263f5b 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.cpp
+++ b/src/core/hle/service/hid/controllers/debug_pad.cpp
@@ -14,7 +14,8 @@ constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
14constexpr s32 HID_JOYSTICK_MIN = -0x7fff; 14constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
15enum class JoystickId : std::size_t { Joystick_Left, Joystick_Right }; 15enum class JoystickId : std::size_t { Joystick_Left, Joystick_Right };
16 16
17Controller_DebugPad::Controller_DebugPad() = default; 17Controller_DebugPad::Controller_DebugPad(Core::System& system)
18 : ControllerBase(system), system(system) {}
18Controller_DebugPad::~Controller_DebugPad() = default; 19Controller_DebugPad::~Controller_DebugPad() = default;
19 20
20void Controller_DebugPad::OnInit() {} 21void Controller_DebugPad::OnInit() {}
diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h
index e584b92ec..6c4de817e 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.h
+++ b/src/core/hle/service/hid/controllers/debug_pad.h
@@ -16,7 +16,7 @@
16namespace Service::HID { 16namespace Service::HID {
17class Controller_DebugPad final : public ControllerBase { 17class Controller_DebugPad final : public ControllerBase {
18public: 18public:
19 Controller_DebugPad(); 19 explicit Controller_DebugPad(Core::System& system);
20 ~Controller_DebugPad() override; 20 ~Controller_DebugPad() override;
21 21
22 // Called when the controller is initialized 22 // Called when the controller is initialized
@@ -89,5 +89,6 @@ private:
89 buttons; 89 buttons;
90 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> 90 std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>
91 analogs; 91 analogs;
92 Core::System& system;
92}; 93};
93} // namespace Service::HID 94} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp
index a179252e3..80da0a0d3 100644
--- a/src/core/hle/service/hid/controllers/gesture.cpp
+++ b/src/core/hle/service/hid/controllers/gesture.cpp
@@ -10,7 +10,8 @@
10namespace Service::HID { 10namespace Service::HID {
11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00; 11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3BA00;
12 12
13Controller_Gesture::Controller_Gesture() = default; 13Controller_Gesture::Controller_Gesture(Core::System& system)
14 : ControllerBase(system), system(system) {}
14Controller_Gesture::~Controller_Gesture() = default; 15Controller_Gesture::~Controller_Gesture() = default;
15 16
16void Controller_Gesture::OnInit() {} 17void Controller_Gesture::OnInit() {}
diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h
index f305fe90f..396897527 100644
--- a/src/core/hle/service/hid/controllers/gesture.h
+++ b/src/core/hle/service/hid/controllers/gesture.h
@@ -12,7 +12,7 @@
12namespace Service::HID { 12namespace Service::HID {
13class Controller_Gesture final : public ControllerBase { 13class Controller_Gesture final : public ControllerBase {
14public: 14public:
15 Controller_Gesture(); 15 explicit Controller_Gesture(Core::System& system);
16 ~Controller_Gesture() override; 16 ~Controller_Gesture() override;
17 17
18 // Called when the controller is initialized 18 // Called when the controller is initialized
@@ -59,5 +59,6 @@ private:
59 std::array<GestureState, 17> gesture_states; 59 std::array<GestureState, 17> gesture_states;
60 }; 60 };
61 SharedMemory shared_memory{}; 61 SharedMemory shared_memory{};
62 Core::System& system;
62}; 63};
63} // namespace Service::HID 64} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp
index 92d7bfb52..e587b2e15 100644
--- a/src/core/hle/service/hid/controllers/keyboard.cpp
+++ b/src/core/hle/service/hid/controllers/keyboard.cpp
@@ -12,7 +12,8 @@ namespace Service::HID {
12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800; 12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
13constexpr u8 KEYS_PER_BYTE = 8; 13constexpr u8 KEYS_PER_BYTE = 8;
14 14
15Controller_Keyboard::Controller_Keyboard() = default; 15Controller_Keyboard::Controller_Keyboard(Core::System& system)
16 : ControllerBase(system), system(system) {}
16Controller_Keyboard::~Controller_Keyboard() = default; 17Controller_Keyboard::~Controller_Keyboard() = default;
17 18
18void Controller_Keyboard::OnInit() {} 19void Controller_Keyboard::OnInit() {}
diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h
index 73cd2c7bb..ef586f7eb 100644
--- a/src/core/hle/service/hid/controllers/keyboard.h
+++ b/src/core/hle/service/hid/controllers/keyboard.h
@@ -15,7 +15,7 @@
15namespace Service::HID { 15namespace Service::HID {
16class Controller_Keyboard final : public ControllerBase { 16class Controller_Keyboard final : public ControllerBase {
17public: 17public:
18 Controller_Keyboard(); 18 explicit Controller_Keyboard(Core::System& system);
19 ~Controller_Keyboard() override; 19 ~Controller_Keyboard() override;
20 20
21 // Called when the controller is initialized 21 // Called when the controller is initialized
@@ -53,5 +53,6 @@ private:
53 keyboard_keys; 53 keyboard_keys;
54 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardMods> 54 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardMods>
55 keyboard_mods; 55 keyboard_mods;
56 Core::System& system;
56}; 57};
57} // namespace Service::HID 58} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp
index 11ab096d9..88f2ca4c1 100644
--- a/src/core/hle/service/hid/controllers/mouse.cpp
+++ b/src/core/hle/service/hid/controllers/mouse.cpp
@@ -11,7 +11,7 @@
11namespace Service::HID { 11namespace Service::HID {
12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400; 12constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3400;
13 13
14Controller_Mouse::Controller_Mouse() = default; 14Controller_Mouse::Controller_Mouse(Core::System& system) : ControllerBase(system), system(system) {}
15Controller_Mouse::~Controller_Mouse() = default; 15Controller_Mouse::~Controller_Mouse() = default;
16 16
17void Controller_Mouse::OnInit() {} 17void Controller_Mouse::OnInit() {}
diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h
index 9d46eecbe..df2da6ae3 100644
--- a/src/core/hle/service/hid/controllers/mouse.h
+++ b/src/core/hle/service/hid/controllers/mouse.h
@@ -14,7 +14,7 @@
14namespace Service::HID { 14namespace Service::HID {
15class Controller_Mouse final : public ControllerBase { 15class Controller_Mouse final : public ControllerBase {
16public: 16public:
17 Controller_Mouse(); 17 explicit Controller_Mouse(Core::System& system);
18 ~Controller_Mouse() override; 18 ~Controller_Mouse() override;
19 19
20 // Called when the controller is initialized 20 // Called when the controller is initialized
@@ -53,5 +53,6 @@ private:
53 std::unique_ptr<Input::MouseDevice> mouse_device; 53 std::unique_ptr<Input::MouseDevice> mouse_device;
54 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeMouseButton::NumMouseButtons> 54 std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeMouseButton::NumMouseButtons>
55 mouse_button_devices; 55 mouse_button_devices;
56 Core::System& system;
56}; 57};
57} // namespace Service::HID 58} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index e47fe8188..a9cd119c4 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -93,7 +93,7 @@ u32 Controller_NPad::IndexToNPad(std::size_t index) {
93 }; 93 };
94} 94}
95 95
96Controller_NPad::Controller_NPad() = default; 96Controller_NPad::Controller_NPad(Core::System& system) : ControllerBase(system), system(system) {}
97Controller_NPad::~Controller_NPad() = default; 97Controller_NPad::~Controller_NPad() = default;
98 98
99void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) { 99void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
@@ -165,12 +165,15 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
165 controller.battery_level[0] = BATTERY_FULL; 165 controller.battery_level[0] = BATTERY_FULL;
166 controller.battery_level[1] = BATTERY_FULL; 166 controller.battery_level[1] = BATTERY_FULL;
167 controller.battery_level[2] = BATTERY_FULL; 167 controller.battery_level[2] = BATTERY_FULL;
168 styleset_changed_events[controller_idx].writable->Signal();
168} 169}
169 170
170void Controller_NPad::OnInit() { 171void Controller_NPad::OnInit() {
171 auto& kernel = Core::System::GetInstance().Kernel(); 172 auto& kernel = system.Kernel();
172 styleset_changed_event = Kernel::WritableEvent::CreateEventPair( 173 for (std::size_t i = 0; i < styleset_changed_events.size(); i++) {
173 kernel, Kernel::ResetType::Automatic, "npad:NpadStyleSetChanged"); 174 styleset_changed_events[i] = Kernel::WritableEvent::CreateEventPair(
175 kernel, Kernel::ResetType::Manual, fmt::format("npad:NpadStyleSetChanged_{}", i));
176 }
174 177
175 if (!IsControllerActivated()) { 178 if (!IsControllerActivated()) {
176 return; 179 return;
@@ -431,7 +434,6 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
431 supported_npad_id_types.clear(); 434 supported_npad_id_types.clear();
432 supported_npad_id_types.resize(length / sizeof(u32)); 435 supported_npad_id_types.resize(length / sizeof(u32));
433 std::memcpy(supported_npad_id_types.data(), data, length); 436 std::memcpy(supported_npad_id_types.data(), data, length);
434 bool had_controller_update = false;
435 for (std::size_t i = 0; i < connected_controllers.size(); i++) { 437 for (std::size_t i = 0; i < connected_controllers.size(); i++) {
436 auto& controller = connected_controllers[i]; 438 auto& controller = connected_controllers[i];
437 if (!controller.is_connected) { 439 if (!controller.is_connected) {
@@ -450,10 +452,6 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
450 controller.type = requested_controller; 452 controller.type = requested_controller;
451 InitNewlyAddedControler(i); 453 InitNewlyAddedControler(i);
452 } 454 }
453 had_controller_update = true;
454 }
455 if (had_controller_update) {
456 styleset_changed_event.writable->Signal();
457 } 455 }
458 } 456 }
459} 457}
@@ -468,7 +466,6 @@ std::size_t Controller_NPad::GetSupportedNPadIdTypesSize() const {
468} 466}
469 467
470void Controller_NPad::SetHoldType(NpadHoldType joy_hold_type) { 468void Controller_NPad::SetHoldType(NpadHoldType joy_hold_type) {
471 styleset_changed_event.writable->Signal();
472 hold_type = joy_hold_type; 469 hold_type = joy_hold_type;
473} 470}
474 471
@@ -479,7 +476,9 @@ Controller_NPad::NpadHoldType Controller_NPad::GetHoldType() const {
479void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) { 476void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) {
480 const std::size_t npad_index = NPadIdToIndex(npad_id); 477 const std::size_t npad_index = NPadIdToIndex(npad_id);
481 ASSERT(npad_index < shared_memory_entries.size()); 478 ASSERT(npad_index < shared_memory_entries.size());
482 shared_memory_entries[npad_index].pad_assignment = assignment_mode; 479 if (shared_memory_entries[npad_index].pad_assignment != assignment_mode) {
480 shared_memory_entries[npad_index].pad_assignment = assignment_mode;
481 }
483} 482}
484 483
485void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids, 484void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
@@ -498,11 +497,12 @@ void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
498 last_processed_vibration = vibrations.back(); 497 last_processed_vibration = vibrations.back();
499} 498}
500 499
501Kernel::SharedPtr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent() const { 500Kernel::SharedPtr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent(
501 u32 npad_id) const {
502 // TODO(ogniK): Figure out the best time to signal this event. This event seems that it should 502 // TODO(ogniK): Figure out the best time to signal this event. This event seems that it should
503 // be signalled at least once, and signaled after a new controller is connected? 503 // be signalled at least once, and signaled after a new controller is connected?
504 styleset_changed_event.writable->Signal(); 504 const auto& styleset_event = styleset_changed_events[NPadIdToIndex(npad_id)];
505 return styleset_changed_event.readable; 505 return styleset_event.readable;
506} 506}
507 507
508Controller_NPad::Vibration Controller_NPad::GetLastVibration() const { 508Controller_NPad::Vibration Controller_NPad::GetLastVibration() const {
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index f28b36806..1bc3d55d6 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -20,7 +20,7 @@ constexpr u32 NPAD_UNKNOWN = 16; // TODO(ogniK): What is this?
20 20
21class Controller_NPad final : public ControllerBase { 21class Controller_NPad final : public ControllerBase {
22public: 22public:
23 Controller_NPad(); 23 explicit Controller_NPad(Core::System& system);
24 ~Controller_NPad() override; 24 ~Controller_NPad() override;
25 25
26 // Called when the controller is initialized 26 // Called when the controller is initialized
@@ -109,7 +109,7 @@ public:
109 void VibrateController(const std::vector<u32>& controller_ids, 109 void VibrateController(const std::vector<u32>& controller_ids,
110 const std::vector<Vibration>& vibrations); 110 const std::vector<Vibration>& vibrations);
111 111
112 Kernel::SharedPtr<Kernel::ReadableEvent> GetStyleSetChangedEvent() const; 112 Kernel::SharedPtr<Kernel::ReadableEvent> GetStyleSetChangedEvent(u32 npad_id) const;
113 Vibration GetLastVibration() const; 113 Vibration GetLastVibration() const;
114 114
115 void AddNewController(NPadControllerType controller); 115 void AddNewController(NPadControllerType controller);
@@ -315,7 +315,8 @@ private:
315 sticks; 315 sticks;
316 std::vector<u32> supported_npad_id_types{}; 316 std::vector<u32> supported_npad_id_types{};
317 NpadHoldType hold_type{NpadHoldType::Vertical}; 317 NpadHoldType hold_type{NpadHoldType::Vertical};
318 Kernel::EventPair styleset_changed_event; 318 // Each controller should have their own styleset changed event
319 std::array<Kernel::EventPair, 10> styleset_changed_events;
319 Vibration last_processed_vibration{}; 320 Vibration last_processed_vibration{};
320 std::array<ControllerHolder, 10> connected_controllers{}; 321 std::array<ControllerHolder, 10> connected_controllers{};
321 bool can_controllers_vibrate{true}; 322 bool can_controllers_vibrate{true};
@@ -327,5 +328,6 @@ private:
327 std::array<ControllerPad, 10> npad_pad_states{}; 328 std::array<ControllerPad, 10> npad_pad_states{};
328 bool IsControllerSupported(NPadControllerType controller); 329 bool IsControllerSupported(NPadControllerType controller);
329 bool is_in_lr_assignment_mode{false}; 330 bool is_in_lr_assignment_mode{false};
331 Core::System& system;
330}; 332};
331} // namespace Service::HID 333} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/stubbed.cpp b/src/core/hle/service/hid/controllers/stubbed.cpp
index 946948f5e..9b829341e 100644
--- a/src/core/hle/service/hid/controllers/stubbed.cpp
+++ b/src/core/hle/service/hid/controllers/stubbed.cpp
@@ -9,7 +9,8 @@
9 9
10namespace Service::HID { 10namespace Service::HID {
11 11
12Controller_Stubbed::Controller_Stubbed() = default; 12Controller_Stubbed::Controller_Stubbed(Core::System& system)
13 : ControllerBase(system), system(system) {}
13Controller_Stubbed::~Controller_Stubbed() = default; 14Controller_Stubbed::~Controller_Stubbed() = default;
14 15
15void Controller_Stubbed::OnInit() {} 16void Controller_Stubbed::OnInit() {}
diff --git a/src/core/hle/service/hid/controllers/stubbed.h b/src/core/hle/service/hid/controllers/stubbed.h
index 24469f03e..37d7d8538 100644
--- a/src/core/hle/service/hid/controllers/stubbed.h
+++ b/src/core/hle/service/hid/controllers/stubbed.h
@@ -10,7 +10,7 @@
10namespace Service::HID { 10namespace Service::HID {
11class Controller_Stubbed final : public ControllerBase { 11class Controller_Stubbed final : public ControllerBase {
12public: 12public:
13 Controller_Stubbed(); 13 explicit Controller_Stubbed(Core::System& system);
14 ~Controller_Stubbed() override; 14 ~Controller_Stubbed() override;
15 15
16 // Called when the controller is initialized 16 // Called when the controller is initialized
@@ -30,5 +30,6 @@ public:
30private: 30private:
31 bool smart_update{}; 31 bool smart_update{};
32 std::size_t common_offset{}; 32 std::size_t common_offset{};
33 Core::System& system;
33}; 34};
34} // namespace Service::HID 35} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp
index 1a8445a43..25912fd69 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.cpp
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -13,7 +13,8 @@
13namespace Service::HID { 13namespace Service::HID {
14constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400; 14constexpr std::size_t SHARED_MEMORY_OFFSET = 0x400;
15 15
16Controller_Touchscreen::Controller_Touchscreen() = default; 16Controller_Touchscreen::Controller_Touchscreen(Core::System& system)
17 : ControllerBase(system), system(system) {}
17Controller_Touchscreen::~Controller_Touchscreen() = default; 18Controller_Touchscreen::~Controller_Touchscreen() = default;
18 19
19void Controller_Touchscreen::OnInit() {} 20void Controller_Touchscreen::OnInit() {}
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
index 76fc340e9..3429c84db 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.h
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -14,7 +14,7 @@
14namespace Service::HID { 14namespace Service::HID {
15class Controller_Touchscreen final : public ControllerBase { 15class Controller_Touchscreen final : public ControllerBase {
16public: 16public:
17 Controller_Touchscreen(); 17 explicit Controller_Touchscreen(Core::System& system);
18 ~Controller_Touchscreen() override; 18 ~Controller_Touchscreen() override;
19 19
20 // Called when the controller is initialized 20 // Called when the controller is initialized
@@ -69,5 +69,6 @@ private:
69 TouchScreenSharedMemory shared_memory{}; 69 TouchScreenSharedMemory shared_memory{};
70 std::unique_ptr<Input::TouchDevice> touch_device; 70 std::unique_ptr<Input::TouchDevice> touch_device;
71 s64_le last_touch{}; 71 s64_le last_touch{};
72 Core::System& system;
72}; 73};
73} // namespace Service::HID 74} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/xpad.cpp b/src/core/hle/service/hid/controllers/xpad.cpp
index 1a9da9576..1bce044b4 100644
--- a/src/core/hle/service/hid/controllers/xpad.cpp
+++ b/src/core/hle/service/hid/controllers/xpad.cpp
@@ -10,7 +10,7 @@
10namespace Service::HID { 10namespace Service::HID {
11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00; 11constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3C00;
12 12
13Controller_XPad::Controller_XPad() = default; 13Controller_XPad::Controller_XPad(Core::System& system) : ControllerBase(system), system(system) {}
14Controller_XPad::~Controller_XPad() = default; 14Controller_XPad::~Controller_XPad() = default;
15 15
16void Controller_XPad::OnInit() {} 16void Controller_XPad::OnInit() {}
diff --git a/src/core/hle/service/hid/controllers/xpad.h b/src/core/hle/service/hid/controllers/xpad.h
index 2864e6617..c445ebec0 100644
--- a/src/core/hle/service/hid/controllers/xpad.h
+++ b/src/core/hle/service/hid/controllers/xpad.h
@@ -12,7 +12,7 @@
12namespace Service::HID { 12namespace Service::HID {
13class Controller_XPad final : public ControllerBase { 13class Controller_XPad final : public ControllerBase {
14public: 14public:
15 Controller_XPad(); 15 explicit Controller_XPad(Core::System& system);
16 ~Controller_XPad() override; 16 ~Controller_XPad() override;
17 17
18 // Called when the controller is initialized 18 // Called when the controller is initialized
@@ -56,5 +56,6 @@ private:
56 }; 56 };
57 static_assert(sizeof(SharedMemory) == 0x1000, "SharedMemory is an invalid size"); 57 static_assert(sizeof(SharedMemory) == 0x1000, "SharedMemory is an invalid size");
58 SharedMemory shared_memory{}; 58 SharedMemory shared_memory{};
59 Core::System& system;
59}; 60};
60} // namespace Service::HID 61} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index f8b1ca816..8d76ba746 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -42,13 +42,14 @@ constexpr s64 accelerometer_update_ticks = static_cast<s64>(Core::Timing::BASE_C
42constexpr s64 gyroscope_update_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 100); 42constexpr s64 gyroscope_update_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 100);
43constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000; 43constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
44 44
45IAppletResource::IAppletResource() : ServiceFramework("IAppletResource") { 45IAppletResource::IAppletResource(Core::System& system)
46 : ServiceFramework("IAppletResource"), system(system) {
46 static const FunctionInfo functions[] = { 47 static const FunctionInfo functions[] = {
47 {0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"}, 48 {0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"},
48 }; 49 };
49 RegisterHandlers(functions); 50 RegisterHandlers(functions);
50 51
51 auto& kernel = Core::System::GetInstance().Kernel(); 52 auto& kernel = system.Kernel();
52 shared_mem = Kernel::SharedMemory::Create( 53 shared_mem = Kernel::SharedMemory::Create(
53 kernel, nullptr, SHARED_MEMORY_SIZE, Kernel::MemoryPermission::ReadWrite, 54 kernel, nullptr, SHARED_MEMORY_SIZE, Kernel::MemoryPermission::ReadWrite,
54 Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "HID:SharedMemory"); 55 Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "HID:SharedMemory");
@@ -74,7 +75,7 @@ IAppletResource::IAppletResource() : ServiceFramework("IAppletResource") {
74 GetController<Controller_Stubbed>(HidController::Unknown3).SetCommonHeaderOffset(0x5000); 75 GetController<Controller_Stubbed>(HidController::Unknown3).SetCommonHeaderOffset(0x5000);
75 76
76 // Register update callbacks 77 // Register update callbacks
77 auto& core_timing = Core::System::GetInstance().CoreTiming(); 78 auto& core_timing = system.CoreTiming();
78 pad_update_event = 79 pad_update_event =
79 core_timing.RegisterEvent("HID::UpdatePadCallback", [this](u64 userdata, s64 cycles_late) { 80 core_timing.RegisterEvent("HID::UpdatePadCallback", [this](u64 userdata, s64 cycles_late) {
80 UpdateControllers(userdata, cycles_late); 81 UpdateControllers(userdata, cycles_late);
@@ -96,7 +97,7 @@ void IAppletResource::DeactivateController(HidController controller) {
96} 97}
97 98
98IAppletResource ::~IAppletResource() { 99IAppletResource ::~IAppletResource() {
99 Core::System::GetInstance().CoreTiming().UnscheduleEvent(pad_update_event, 0); 100 system.CoreTiming().UnscheduleEvent(pad_update_event, 0);
100} 101}
101 102
102void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) { 103void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
@@ -108,7 +109,7 @@ void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
108} 109}
109 110
110void IAppletResource::UpdateControllers(u64 userdata, s64 cycles_late) { 111void IAppletResource::UpdateControllers(u64 userdata, s64 cycles_late) {
111 auto& core_timing = Core::System::GetInstance().CoreTiming(); 112 auto& core_timing = system.CoreTiming();
112 113
113 const bool should_reload = Settings::values.is_device_reload_pending.exchange(false); 114 const bool should_reload = Settings::values.is_device_reload_pending.exchange(false);
114 for (const auto& controller : controllers) { 115 for (const auto& controller : controllers) {
@@ -141,13 +142,13 @@ private:
141 142
142std::shared_ptr<IAppletResource> Hid::GetAppletResource() { 143std::shared_ptr<IAppletResource> Hid::GetAppletResource() {
143 if (applet_resource == nullptr) { 144 if (applet_resource == nullptr) {
144 applet_resource = std::make_shared<IAppletResource>(); 145 applet_resource = std::make_shared<IAppletResource>(system);
145 } 146 }
146 147
147 return applet_resource; 148 return applet_resource;
148} 149}
149 150
150Hid::Hid() : ServiceFramework("hid") { 151Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
151 // clang-format off 152 // clang-format off
152 static const FunctionInfo functions[] = { 153 static const FunctionInfo functions[] = {
153 {0, &Hid::CreateAppletResource, "CreateAppletResource"}, 154 {0, &Hid::CreateAppletResource, "CreateAppletResource"},
@@ -286,7 +287,7 @@ void Hid::CreateAppletResource(Kernel::HLERequestContext& ctx) {
286 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id); 287 LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
287 288
288 if (applet_resource == nullptr) { 289 if (applet_resource == nullptr) {
289 applet_resource = std::make_shared<IAppletResource>(); 290 applet_resource = std::make_shared<IAppletResource>(system);
290 } 291 }
291 292
292 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 293 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
@@ -479,7 +480,7 @@ void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
479 IPC::ResponseBuilder rb{ctx, 2, 1}; 480 IPC::ResponseBuilder rb{ctx, 2, 1};
480 rb.Push(RESULT_SUCCESS); 481 rb.Push(RESULT_SUCCESS);
481 rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad) 482 rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad)
482 .GetStyleSetChangedEvent()); 483 .GetStyleSetChangedEvent(npad_id));
483} 484}
484 485
485void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) { 486void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) {
@@ -1053,14 +1054,14 @@ void ReloadInputDevices() {
1053 Settings::values.is_device_reload_pending.store(true); 1054 Settings::values.is_device_reload_pending.store(true);
1054} 1055}
1055 1056
1056void InstallInterfaces(SM::ServiceManager& service_manager) { 1057void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
1057 std::make_shared<Hid>()->InstallAsService(service_manager); 1058 std::make_shared<Hid>(system)->InstallAsService(service_manager);
1058 std::make_shared<HidBus>()->InstallAsService(service_manager); 1059 std::make_shared<HidBus>()->InstallAsService(service_manager);
1059 std::make_shared<HidDbg>()->InstallAsService(service_manager); 1060 std::make_shared<HidDbg>()->InstallAsService(service_manager);
1060 std::make_shared<HidSys>()->InstallAsService(service_manager); 1061 std::make_shared<HidSys>()->InstallAsService(service_manager);
1061 std::make_shared<HidTmp>()->InstallAsService(service_manager); 1062 std::make_shared<HidTmp>()->InstallAsService(service_manager);
1062 1063
1063 std::make_shared<IRS>()->InstallAsService(service_manager); 1064 std::make_shared<IRS>(system)->InstallAsService(service_manager);
1064 std::make_shared<IRS_SYS>()->InstallAsService(service_manager); 1065 std::make_shared<IRS_SYS>()->InstallAsService(service_manager);
1065 1066
1066 std::make_shared<XCD_SYS>()->InstallAsService(service_manager); 1067 std::make_shared<XCD_SYS>()->InstallAsService(service_manager);
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 2fd6d9fc7..35b663679 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -42,7 +42,7 @@ enum class HidController : std::size_t {
42 42
43class IAppletResource final : public ServiceFramework<IAppletResource> { 43class IAppletResource final : public ServiceFramework<IAppletResource> {
44public: 44public:
45 IAppletResource(); 45 explicit IAppletResource(Core::System& system);
46 ~IAppletResource() override; 46 ~IAppletResource() override;
47 47
48 void ActivateController(HidController controller); 48 void ActivateController(HidController controller);
@@ -61,7 +61,7 @@ public:
61private: 61private:
62 template <typename T> 62 template <typename T>
63 void MakeController(HidController controller) { 63 void MakeController(HidController controller) {
64 controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>(); 64 controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>(system);
65 } 65 }
66 66
67 void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx); 67 void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx);
@@ -70,6 +70,7 @@ private:
70 Kernel::SharedPtr<Kernel::SharedMemory> shared_mem; 70 Kernel::SharedPtr<Kernel::SharedMemory> shared_mem;
71 71
72 Core::Timing::EventType* pad_update_event; 72 Core::Timing::EventType* pad_update_event;
73 Core::System& system;
73 74
74 std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)> 75 std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)>
75 controllers{}; 76 controllers{};
@@ -77,7 +78,7 @@ private:
77 78
78class Hid final : public ServiceFramework<Hid> { 79class Hid final : public ServiceFramework<Hid> {
79public: 80public:
80 Hid(); 81 explicit Hid(Core::System& system);
81 ~Hid() override; 82 ~Hid() override;
82 83
83 std::shared_ptr<IAppletResource> GetAppletResource(); 84 std::shared_ptr<IAppletResource> GetAppletResource();
@@ -126,12 +127,13 @@ private:
126 void SwapNpadAssignment(Kernel::HLERequestContext& ctx); 127 void SwapNpadAssignment(Kernel::HLERequestContext& ctx);
127 128
128 std::shared_ptr<IAppletResource> applet_resource; 129 std::shared_ptr<IAppletResource> applet_resource;
130 Core::System& system;
129}; 131};
130 132
131/// Reload input devices. Used when input configuration changed 133/// Reload input devices. Used when input configuration changed
132void ReloadInputDevices(); 134void ReloadInputDevices();
133 135
134/// Registers all HID services with the specified service manager. 136/// Registers all HID services with the specified service manager.
135void InstallInterfaces(SM::ServiceManager& service_manager); 137void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
136 138
137} // namespace Service::HID 139} // namespace Service::HID
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp
index 2c4625c99..5e79e2c1a 100644
--- a/src/core/hle/service/hid/irs.cpp
+++ b/src/core/hle/service/hid/irs.cpp
@@ -11,7 +11,7 @@
11 11
12namespace Service::HID { 12namespace Service::HID {
13 13
14IRS::IRS() : ServiceFramework{"irs"} { 14IRS::IRS(Core::System& system) : ServiceFramework{"irs"}, system(system) {
15 // clang-format off 15 // clang-format off
16 static const FunctionInfo functions[] = { 16 static const FunctionInfo functions[] = {
17 {302, &IRS::ActivateIrsensor, "ActivateIrsensor"}, 17 {302, &IRS::ActivateIrsensor, "ActivateIrsensor"},
@@ -37,7 +37,7 @@ IRS::IRS() : ServiceFramework{"irs"} {
37 37
38 RegisterHandlers(functions); 38 RegisterHandlers(functions);
39 39
40 auto& kernel = Core::System::GetInstance().Kernel(); 40 auto& kernel = system.Kernel();
41 shared_mem = Kernel::SharedMemory::Create( 41 shared_mem = Kernel::SharedMemory::Create(
42 kernel, nullptr, 0x8000, Kernel::MemoryPermission::ReadWrite, 42 kernel, nullptr, 0x8000, Kernel::MemoryPermission::ReadWrite,
43 Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "IRS:SharedMemory"); 43 Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "IRS:SharedMemory");
@@ -98,7 +98,7 @@ void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
98 98
99 IPC::ResponseBuilder rb{ctx, 5}; 99 IPC::ResponseBuilder rb{ctx, 5};
100 rb.Push(RESULT_SUCCESS); 100 rb.Push(RESULT_SUCCESS);
101 rb.PushRaw<u64>(Core::System::GetInstance().CoreTiming().GetTicks()); 101 rb.PushRaw<u64>(system.CoreTiming().GetTicks());
102 rb.PushRaw<u32>(0); 102 rb.PushRaw<u32>(0);
103} 103}
104 104
diff --git a/src/core/hle/service/hid/irs.h b/src/core/hle/service/hid/irs.h
index 12de6bfb3..eb4e898dd 100644
--- a/src/core/hle/service/hid/irs.h
+++ b/src/core/hle/service/hid/irs.h
@@ -15,7 +15,7 @@ namespace Service::HID {
15 15
16class IRS final : public ServiceFramework<IRS> { 16class IRS final : public ServiceFramework<IRS> {
17public: 17public:
18 explicit IRS(); 18 explicit IRS(Core::System& system);
19 ~IRS() override; 19 ~IRS() override;
20 20
21private: 21private:
@@ -39,6 +39,7 @@ private:
39 void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx); 39 void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx);
40 Kernel::SharedPtr<Kernel::SharedMemory> shared_mem; 40 Kernel::SharedPtr<Kernel::SharedMemory> shared_mem;
41 const u32 device_handle{0xABCD}; 41 const u32 device_handle{0xABCD};
42 Core::System& system;
42}; 43};
43 44
44class IRS_SYS final : public ServiceFramework<IRS_SYS> { 45class IRS_SYS final : public ServiceFramework<IRS_SYS> {
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index 8ddad8682..3164ca26e 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -78,7 +78,7 @@ public:
78 78
79class RelocatableObject final : public ServiceFramework<RelocatableObject> { 79class RelocatableObject final : public ServiceFramework<RelocatableObject> {
80public: 80public:
81 explicit RelocatableObject() : ServiceFramework{"ldr:ro"} { 81 explicit RelocatableObject(Core::System& system) : ServiceFramework{"ldr:ro"}, system(system) {
82 // clang-format off 82 // clang-format off
83 static const FunctionInfo functions[] = { 83 static const FunctionInfo functions[] = {
84 {0, &RelocatableObject::LoadNro, "LoadNro"}, 84 {0, &RelocatableObject::LoadNro, "LoadNro"},
@@ -364,7 +364,7 @@ public:
364 vm_manager.ReprotectRange(*map_address + header.rw_offset, header.rw_size, 364 vm_manager.ReprotectRange(*map_address + header.rw_offset, header.rw_size,
365 Kernel::VMAPermission::ReadWrite); 365 Kernel::VMAPermission::ReadWrite);
366 366
367 Core::System::GetInstance().InvalidateCpuInstructionCaches(); 367 system.InvalidateCpuInstructionCaches();
368 368
369 nro.insert_or_assign(*map_address, 369 nro.insert_or_assign(*map_address,
370 NROInfo{hash, nro_address, nro_size, bss_address, bss_size}); 370 NROInfo{hash, nro_address, nro_size, bss_address, bss_size});
@@ -430,7 +430,7 @@ public:
430 .IsSuccess()); 430 .IsSuccess());
431 } 431 }
432 432
433 Core::System::GetInstance().InvalidateCpuInstructionCaches(); 433 system.InvalidateCpuInstructionCaches();
434 434
435 nro.erase(iter); 435 nro.erase(iter);
436 IPC::ResponseBuilder rb{ctx, 2}; 436 IPC::ResponseBuilder rb{ctx, 2};
@@ -516,13 +516,14 @@ private:
516 Common::Is4KBAligned(header.text_size) && Common::Is4KBAligned(header.ro_size) && 516 Common::Is4KBAligned(header.text_size) && Common::Is4KBAligned(header.ro_size) &&
517 Common::Is4KBAligned(header.rw_size); 517 Common::Is4KBAligned(header.rw_size);
518 } 518 }
519 Core::System& system;
519}; 520};
520 521
521void InstallInterfaces(SM::ServiceManager& sm) { 522void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
522 std::make_shared<DebugMonitor>()->InstallAsService(sm); 523 std::make_shared<DebugMonitor>()->InstallAsService(sm);
523 std::make_shared<ProcessManager>()->InstallAsService(sm); 524 std::make_shared<ProcessManager>()->InstallAsService(sm);
524 std::make_shared<Shell>()->InstallAsService(sm); 525 std::make_shared<Shell>()->InstallAsService(sm);
525 std::make_shared<RelocatableObject>()->InstallAsService(sm); 526 std::make_shared<RelocatableObject>(system)->InstallAsService(sm);
526} 527}
527 528
528} // namespace Service::LDR 529} // namespace Service::LDR
diff --git a/src/core/hle/service/ldr/ldr.h b/src/core/hle/service/ldr/ldr.h
index 412410c4f..7ac8c0b65 100644
--- a/src/core/hle/service/ldr/ldr.h
+++ b/src/core/hle/service/ldr/ldr.h
@@ -11,6 +11,6 @@ class ServiceManager;
11namespace Service::LDR { 11namespace Service::LDR {
12 12
13/// Registers all LDR services with the specified service manager. 13/// Registers all LDR services with the specified service manager.
14void InstallInterfaces(SM::ServiceManager& sm); 14void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
15 15
16} // namespace Service::LDR 16} // namespace Service::LDR
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index a5cb06f8a..a42c22d44 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -23,9 +23,9 @@ constexpr ResultCode ERR_TAG_FAILED(ErrorModule::NFP,
23constexpr ResultCode ERR_NO_APPLICATION_AREA(ErrorModule::NFP, 152); 23constexpr ResultCode ERR_NO_APPLICATION_AREA(ErrorModule::NFP, 152);
24} // namespace ErrCodes 24} // namespace ErrCodes
25 25
26Module::Interface::Interface(std::shared_ptr<Module> module, const char* name) 26Module::Interface::Interface(std::shared_ptr<Module> module, Core::System& system, const char* name)
27 : ServiceFramework(name), module(std::move(module)) { 27 : ServiceFramework(name), module(std::move(module)), system(system) {
28 auto& kernel = Core::System::GetInstance().Kernel(); 28 auto& kernel = system.Kernel();
29 nfc_tag_load = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic, 29 nfc_tag_load = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
30 "IUser:NFCTagDetected"); 30 "IUser:NFCTagDetected");
31} 31}
@@ -34,8 +34,8 @@ Module::Interface::~Interface() = default;
34 34
35class IUser final : public ServiceFramework<IUser> { 35class IUser final : public ServiceFramework<IUser> {
36public: 36public:
37 IUser(Module::Interface& nfp_interface) 37 IUser(Module::Interface& nfp_interface, Core::System& system)
38 : ServiceFramework("NFP::IUser"), nfp_interface(nfp_interface) { 38 : ServiceFramework("NFP::IUser"), nfp_interface(nfp_interface), system(system) {
39 static const FunctionInfo functions[] = { 39 static const FunctionInfo functions[] = {
40 {0, &IUser::Initialize, "Initialize"}, 40 {0, &IUser::Initialize, "Initialize"},
41 {1, &IUser::Finalize, "Finalize"}, 41 {1, &IUser::Finalize, "Finalize"},
@@ -65,7 +65,7 @@ public:
65 }; 65 };
66 RegisterHandlers(functions); 66 RegisterHandlers(functions);
67 67
68 auto& kernel = Core::System::GetInstance().Kernel(); 68 auto& kernel = system.Kernel();
69 deactivate_event = Kernel::WritableEvent::CreateEventPair( 69 deactivate_event = Kernel::WritableEvent::CreateEventPair(
70 kernel, Kernel::ResetType::Automatic, "IUser:DeactivateEvent"); 70 kernel, Kernel::ResetType::Automatic, "IUser:DeactivateEvent");
71 availability_change_event = Kernel::WritableEvent::CreateEventPair( 71 availability_change_event = Kernel::WritableEvent::CreateEventPair(
@@ -324,6 +324,7 @@ private:
324 Kernel::EventPair deactivate_event; 324 Kernel::EventPair deactivate_event;
325 Kernel::EventPair availability_change_event; 325 Kernel::EventPair availability_change_event;
326 const Module::Interface& nfp_interface; 326 const Module::Interface& nfp_interface;
327 Core::System& system;
327}; 328};
328 329
329void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) { 330void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {
@@ -331,7 +332,7 @@ void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {
331 332
332 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 333 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
333 rb.Push(RESULT_SUCCESS); 334 rb.Push(RESULT_SUCCESS);
334 rb.PushIpcInterface<IUser>(*this); 335 rb.PushIpcInterface<IUser>(*this, system);
335} 336}
336 337
337bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) { 338bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) {
@@ -353,9 +354,9 @@ const Module::Interface::AmiiboFile& Module::Interface::GetAmiiboBuffer() const
353 return amiibo; 354 return amiibo;
354} 355}
355 356
356void InstallInterfaces(SM::ServiceManager& service_manager) { 357void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
357 auto module = std::make_shared<Module>(); 358 auto module = std::make_shared<Module>();
358 std::make_shared<NFP_User>(module)->InstallAsService(service_manager); 359 std::make_shared<NFP_User>(module, system)->InstallAsService(service_manager);
359} 360}
360 361
361} // namespace Service::NFP 362} // namespace Service::NFP
diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h
index a1817e991..9718ef745 100644
--- a/src/core/hle/service/nfp/nfp.h
+++ b/src/core/hle/service/nfp/nfp.h
@@ -16,7 +16,7 @@ class Module final {
16public: 16public:
17 class Interface : public ServiceFramework<Interface> { 17 class Interface : public ServiceFramework<Interface> {
18 public: 18 public:
19 explicit Interface(std::shared_ptr<Module> module, const char* name); 19 explicit Interface(std::shared_ptr<Module> module, Core::System& system, const char* name);
20 ~Interface() override; 20 ~Interface() override;
21 21
22 struct ModelInfo { 22 struct ModelInfo {
@@ -43,9 +43,10 @@ public:
43 43
44 protected: 44 protected:
45 std::shared_ptr<Module> module; 45 std::shared_ptr<Module> module;
46 Core::System& system;
46 }; 47 };
47}; 48};
48 49
49void InstallInterfaces(SM::ServiceManager& service_manager); 50void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
50 51
51} // namespace Service::NFP 52} // namespace Service::NFP
diff --git a/src/core/hle/service/nfp/nfp_user.cpp b/src/core/hle/service/nfp/nfp_user.cpp
index 784a87c1b..298184f17 100644
--- a/src/core/hle/service/nfp/nfp_user.cpp
+++ b/src/core/hle/service/nfp/nfp_user.cpp
@@ -6,8 +6,8 @@
6 6
7namespace Service::NFP { 7namespace Service::NFP {
8 8
9NFP_User::NFP_User(std::shared_ptr<Module> module) 9NFP_User::NFP_User(std::shared_ptr<Module> module, Core::System& system)
10 : Module::Interface(std::move(module), "nfp:user") { 10 : Module::Interface(std::move(module), system, "nfp:user") {
11 static const FunctionInfo functions[] = { 11 static const FunctionInfo functions[] = {
12 {0, &NFP_User::CreateUserInterface, "CreateUserInterface"}, 12 {0, &NFP_User::CreateUserInterface, "CreateUserInterface"},
13 }; 13 };
diff --git a/src/core/hle/service/nfp/nfp_user.h b/src/core/hle/service/nfp/nfp_user.h
index 65d9aaf48..1686ebf20 100644
--- a/src/core/hle/service/nfp/nfp_user.h
+++ b/src/core/hle/service/nfp/nfp_user.h
@@ -10,7 +10,7 @@ namespace Service::NFP {
10 10
11class NFP_User final : public Module::Interface { 11class NFP_User final : public Module::Interface {
12public: 12public:
13 explicit NFP_User(std::shared_ptr<Module> module); 13 explicit NFP_User(std::shared_ptr<Module> module, Core::System& system);
14 ~NFP_User() override; 14 ~NFP_User() override;
15}; 15};
16 16
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index 76b12b482..756a2af57 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -12,6 +12,13 @@
12 12
13namespace Service::NIFM { 13namespace Service::NIFM {
14 14
15enum class RequestState : u32 {
16 NotSubmitted = 1,
17 Error = 1, ///< The duplicate 1 is intentional; it means both not submitted and error on HW.
18 Pending = 2,
19 Connected = 3,
20};
21
15class IScanRequest final : public ServiceFramework<IScanRequest> { 22class IScanRequest final : public ServiceFramework<IScanRequest> {
16public: 23public:
17 explicit IScanRequest() : ServiceFramework("IScanRequest") { 24 explicit IScanRequest() : ServiceFramework("IScanRequest") {
@@ -31,7 +38,7 @@ public:
31 38
32class IRequest final : public ServiceFramework<IRequest> { 39class IRequest final : public ServiceFramework<IRequest> {
33public: 40public:
34 explicit IRequest() : ServiceFramework("IRequest") { 41 explicit IRequest(Core::System& system) : ServiceFramework("IRequest") {
35 static const FunctionInfo functions[] = { 42 static const FunctionInfo functions[] = {
36 {0, &IRequest::GetRequestState, "GetRequestState"}, 43 {0, &IRequest::GetRequestState, "GetRequestState"},
37 {1, &IRequest::GetResult, "GetResult"}, 44 {1, &IRequest::GetResult, "GetResult"},
@@ -61,7 +68,7 @@ public:
61 }; 68 };
62 RegisterHandlers(functions); 69 RegisterHandlers(functions);
63 70
64 auto& kernel = Core::System::GetInstance().Kernel(); 71 auto& kernel = system.Kernel();
65 event1 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic, 72 event1 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
66 "IRequest:Event1"); 73 "IRequest:Event1");
67 event2 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic, 74 event2 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic,
@@ -81,7 +88,7 @@ private:
81 88
82 IPC::ResponseBuilder rb{ctx, 3}; 89 IPC::ResponseBuilder rb{ctx, 3};
83 rb.Push(RESULT_SUCCESS); 90 rb.Push(RESULT_SUCCESS);
84 rb.Push<u32>(0); 91 rb.PushEnum(RequestState::Connected);
85 } 92 }
86 93
87 void GetResult(Kernel::HLERequestContext& ctx) { 94 void GetResult(Kernel::HLERequestContext& ctx) {
@@ -130,7 +137,7 @@ public:
130 137
131class IGeneralService final : public ServiceFramework<IGeneralService> { 138class IGeneralService final : public ServiceFramework<IGeneralService> {
132public: 139public:
133 IGeneralService(); 140 IGeneralService(Core::System& system);
134 141
135private: 142private:
136 void GetClientId(Kernel::HLERequestContext& ctx) { 143 void GetClientId(Kernel::HLERequestContext& ctx) {
@@ -155,7 +162,7 @@ private:
155 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 162 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
156 163
157 rb.Push(RESULT_SUCCESS); 164 rb.Push(RESULT_SUCCESS);
158 rb.PushIpcInterface<IRequest>(); 165 rb.PushIpcInterface<IRequest>(system);
159 } 166 }
160 void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) { 167 void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) {
161 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 168 LOG_WARNING(Service_NIFM, "(STUBBED) called");
@@ -189,18 +196,20 @@ private:
189 196
190 IPC::ResponseBuilder rb{ctx, 3}; 197 IPC::ResponseBuilder rb{ctx, 3};
191 rb.Push(RESULT_SUCCESS); 198 rb.Push(RESULT_SUCCESS);
192 rb.Push<u8>(0); 199 rb.Push<u8>(1);
193 } 200 }
194 void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) { 201 void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) {
195 LOG_WARNING(Service_NIFM, "(STUBBED) called"); 202 LOG_WARNING(Service_NIFM, "(STUBBED) called");
196 203
197 IPC::ResponseBuilder rb{ctx, 3}; 204 IPC::ResponseBuilder rb{ctx, 3};
198 rb.Push(RESULT_SUCCESS); 205 rb.Push(RESULT_SUCCESS);
199 rb.Push<u8>(0); 206 rb.Push<u8>(1);
200 } 207 }
208 Core::System& system;
201}; 209};
202 210
203IGeneralService::IGeneralService() : ServiceFramework("IGeneralService") { 211IGeneralService::IGeneralService(Core::System& system)
212 : ServiceFramework("IGeneralService"), system(system) {
204 static const FunctionInfo functions[] = { 213 static const FunctionInfo functions[] = {
205 {1, &IGeneralService::GetClientId, "GetClientId"}, 214 {1, &IGeneralService::GetClientId, "GetClientId"},
206 {2, &IGeneralService::CreateScanRequest, "CreateScanRequest"}, 215 {2, &IGeneralService::CreateScanRequest, "CreateScanRequest"},
@@ -245,7 +254,8 @@ IGeneralService::IGeneralService() : ServiceFramework("IGeneralService") {
245 254
246class NetworkInterface final : public ServiceFramework<NetworkInterface> { 255class NetworkInterface final : public ServiceFramework<NetworkInterface> {
247public: 256public:
248 explicit NetworkInterface(const char* name) : ServiceFramework{name} { 257 explicit NetworkInterface(const char* name, Core::System& system)
258 : ServiceFramework{name}, system(system) {
249 static const FunctionInfo functions[] = { 259 static const FunctionInfo functions[] = {
250 {4, &NetworkInterface::CreateGeneralServiceOld, "CreateGeneralServiceOld"}, 260 {4, &NetworkInterface::CreateGeneralServiceOld, "CreateGeneralServiceOld"},
251 {5, &NetworkInterface::CreateGeneralService, "CreateGeneralService"}, 261 {5, &NetworkInterface::CreateGeneralService, "CreateGeneralService"},
@@ -258,7 +268,7 @@ public:
258 268
259 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 269 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
260 rb.Push(RESULT_SUCCESS); 270 rb.Push(RESULT_SUCCESS);
261 rb.PushIpcInterface<IGeneralService>(); 271 rb.PushIpcInterface<IGeneralService>(system);
262 } 272 }
263 273
264 void CreateGeneralService(Kernel::HLERequestContext& ctx) { 274 void CreateGeneralService(Kernel::HLERequestContext& ctx) {
@@ -266,14 +276,17 @@ public:
266 276
267 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 277 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
268 rb.Push(RESULT_SUCCESS); 278 rb.Push(RESULT_SUCCESS);
269 rb.PushIpcInterface<IGeneralService>(); 279 rb.PushIpcInterface<IGeneralService>(system);
270 } 280 }
281
282private:
283 Core::System& system;
271}; 284};
272 285
273void InstallInterfaces(SM::ServiceManager& service_manager) { 286void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
274 std::make_shared<NetworkInterface>("nifm:a")->InstallAsService(service_manager); 287 std::make_shared<NetworkInterface>("nifm:a", system)->InstallAsService(service_manager);
275 std::make_shared<NetworkInterface>("nifm:s")->InstallAsService(service_manager); 288 std::make_shared<NetworkInterface>("nifm:s", system)->InstallAsService(service_manager);
276 std::make_shared<NetworkInterface>("nifm:u")->InstallAsService(service_manager); 289 std::make_shared<NetworkInterface>("nifm:u", system)->InstallAsService(service_manager);
277} 290}
278 291
279} // namespace Service::NIFM 292} // namespace Service::NIFM
diff --git a/src/core/hle/service/nifm/nifm.h b/src/core/hle/service/nifm/nifm.h
index 4616b3b48..6857e18f9 100644
--- a/src/core/hle/service/nifm/nifm.h
+++ b/src/core/hle/service/nifm/nifm.h
@@ -8,9 +8,13 @@ namespace Service::SM {
8class ServiceManager; 8class ServiceManager;
9} 9}
10 10
11namespace Core {
12class System;
13}
14
11namespace Service::NIFM { 15namespace Service::NIFM {
12 16
13/// Registers all NIFM services with the specified service manager. 17/// Registers all NIFM services with the specified service manager.
14void InstallInterfaces(SM::ServiceManager& service_manager); 18void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
15 19
16} // namespace Service::NIFM 20} // namespace Service::NIFM
diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp
index f319a3ca1..75d414952 100644
--- a/src/core/hle/service/nim/nim.cpp
+++ b/src/core/hle/service/nim/nim.cpp
@@ -126,7 +126,7 @@ public:
126class IEnsureNetworkClockAvailabilityService final 126class IEnsureNetworkClockAvailabilityService final
127 : public ServiceFramework<IEnsureNetworkClockAvailabilityService> { 127 : public ServiceFramework<IEnsureNetworkClockAvailabilityService> {
128public: 128public:
129 IEnsureNetworkClockAvailabilityService() 129 explicit IEnsureNetworkClockAvailabilityService(Core::System& system)
130 : ServiceFramework("IEnsureNetworkClockAvailabilityService") { 130 : ServiceFramework("IEnsureNetworkClockAvailabilityService") {
131 static const FunctionInfo functions[] = { 131 static const FunctionInfo functions[] = {
132 {0, &IEnsureNetworkClockAvailabilityService::StartTask, "StartTask"}, 132 {0, &IEnsureNetworkClockAvailabilityService::StartTask, "StartTask"},
@@ -139,7 +139,7 @@ public:
139 }; 139 };
140 RegisterHandlers(functions); 140 RegisterHandlers(functions);
141 141
142 auto& kernel = Core::System::GetInstance().Kernel(); 142 auto& kernel = system.Kernel();
143 finished_event = Kernel::WritableEvent::CreateEventPair( 143 finished_event = Kernel::WritableEvent::CreateEventPair(
144 kernel, Kernel::ResetType::Automatic, 144 kernel, Kernel::ResetType::Automatic,
145 "IEnsureNetworkClockAvailabilityService:FinishEvent"); 145 "IEnsureNetworkClockAvailabilityService:FinishEvent");
@@ -200,7 +200,7 @@ private:
200 200
201class NTC final : public ServiceFramework<NTC> { 201class NTC final : public ServiceFramework<NTC> {
202public: 202public:
203 explicit NTC() : ServiceFramework{"ntc"} { 203 explicit NTC(Core::System& system) : ServiceFramework{"ntc"}, system(system) {
204 // clang-format off 204 // clang-format off
205 static const FunctionInfo functions[] = { 205 static const FunctionInfo functions[] = {
206 {0, &NTC::OpenEnsureNetworkClockAvailabilityService, "OpenEnsureNetworkClockAvailabilityService"}, 206 {0, &NTC::OpenEnsureNetworkClockAvailabilityService, "OpenEnsureNetworkClockAvailabilityService"},
@@ -218,7 +218,7 @@ private:
218 218
219 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 219 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
220 rb.Push(RESULT_SUCCESS); 220 rb.Push(RESULT_SUCCESS);
221 rb.PushIpcInterface<IEnsureNetworkClockAvailabilityService>(); 221 rb.PushIpcInterface<IEnsureNetworkClockAvailabilityService>(system);
222 } 222 }
223 223
224 // TODO(ogniK): Do we need these? 224 // TODO(ogniK): Do we need these?
@@ -235,13 +235,14 @@ private:
235 IPC::ResponseBuilder rb{ctx, 2}; 235 IPC::ResponseBuilder rb{ctx, 2};
236 rb.Push(RESULT_SUCCESS); 236 rb.Push(RESULT_SUCCESS);
237 } 237 }
238 Core::System& system;
238}; 239};
239 240
240void InstallInterfaces(SM::ServiceManager& sm) { 241void InstallInterfaces(SM::ServiceManager& sm, Core::System& system) {
241 std::make_shared<NIM>()->InstallAsService(sm); 242 std::make_shared<NIM>()->InstallAsService(sm);
242 std::make_shared<NIM_ECA>()->InstallAsService(sm); 243 std::make_shared<NIM_ECA>()->InstallAsService(sm);
243 std::make_shared<NIM_SHP>()->InstallAsService(sm); 244 std::make_shared<NIM_SHP>()->InstallAsService(sm);
244 std::make_shared<NTC>()->InstallAsService(sm); 245 std::make_shared<NTC>(system)->InstallAsService(sm);
245} 246}
246 247
247} // namespace Service::NIM 248} // namespace Service::NIM
diff --git a/src/core/hle/service/nim/nim.h b/src/core/hle/service/nim/nim.h
index 2a2a92df0..dbe25dc01 100644
--- a/src/core/hle/service/nim/nim.h
+++ b/src/core/hle/service/nim/nim.h
@@ -8,8 +8,12 @@ namespace Service::SM {
8class ServiceManager; 8class ServiceManager;
9} 9}
10 10
11namespace Core {
12class System;
13}
14
11namespace Service::NIM { 15namespace Service::NIM {
12 16
13void InstallInterfaces(SM::ServiceManager& sm); 17void InstallInterfaces(SM::ServiceManager& sm, Core::System& system);
14 18
15} // namespace Service::NIM 19} // namespace Service::NIM
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
index 13121c4f1..15c156ce1 100644
--- a/src/core/hle/service/ns/ns.cpp
+++ b/src/core/hle/service/ns/ns.cpp
@@ -617,7 +617,8 @@ public:
617 } 617 }
618}; 618};
619 619
620void InstallInterfaces(SM::ServiceManager& service_manager, FileSystem::FileSystemController& fsc) { 620void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
621
621 std::make_shared<NS>("ns:am2")->InstallAsService(service_manager); 622 std::make_shared<NS>("ns:am2")->InstallAsService(service_manager);
622 std::make_shared<NS>("ns:ec")->InstallAsService(service_manager); 623 std::make_shared<NS>("ns:ec")->InstallAsService(service_manager);
623 std::make_shared<NS>("ns:rid")->InstallAsService(service_manager); 624 std::make_shared<NS>("ns:rid")->InstallAsService(service_manager);
@@ -628,7 +629,7 @@ void InstallInterfaces(SM::ServiceManager& service_manager, FileSystem::FileSyst
628 std::make_shared<NS_SU>()->InstallAsService(service_manager); 629 std::make_shared<NS_SU>()->InstallAsService(service_manager);
629 std::make_shared<NS_VM>()->InstallAsService(service_manager); 630 std::make_shared<NS_VM>()->InstallAsService(service_manager);
630 631
631 std::make_shared<PL_U>(fsc)->InstallAsService(service_manager); 632 std::make_shared<PL_U>(system)->InstallAsService(service_manager);
632} 633}
633 634
634} // namespace Service::NS 635} // namespace Service::NS
diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h
index d067e7a9a..13a64ad88 100644
--- a/src/core/hle/service/ns/ns.h
+++ b/src/core/hle/service/ns/ns.h
@@ -97,8 +97,7 @@ private:
97}; 97};
98 98
99/// Registers all NS services with the specified service manager. 99/// Registers all NS services with the specified service manager.
100void InstallInterfaces(SM::ServiceManager& service_manager, FileSystem::FileSystemController& fsc); 100void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
101 101
102} // namespace NS 102} // namespace NS
103
104} // namespace Service 103} // namespace Service
diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp
index 9d49f36e8..7dcdb4a07 100644
--- a/src/core/hle/service/ns/pl_u.cpp
+++ b/src/core/hle/service/ns/pl_u.cpp
@@ -150,8 +150,9 @@ struct PL_U::Impl {
150 std::vector<FontRegion> shared_font_regions; 150 std::vector<FontRegion> shared_font_regions;
151}; 151};
152 152
153PL_U::PL_U(FileSystem::FileSystemController& fsc) 153PL_U::PL_U(Core::System& system)
154 : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()} { 154 : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()}, system(system) {
155
155 static const FunctionInfo functions[] = { 156 static const FunctionInfo functions[] = {
156 {0, &PL_U::RequestLoad, "RequestLoad"}, 157 {0, &PL_U::RequestLoad, "RequestLoad"},
157 {1, &PL_U::GetLoadState, "GetLoadState"}, 158 {1, &PL_U::GetLoadState, "GetLoadState"},
@@ -161,6 +162,9 @@ PL_U::PL_U(FileSystem::FileSystemController& fsc)
161 {5, &PL_U::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriority"}, 162 {5, &PL_U::GetSharedFontInOrderOfPriority, "GetSharedFontInOrderOfPriority"},
162 }; 163 };
163 RegisterHandlers(functions); 164 RegisterHandlers(functions);
165
166 auto& fsc = system.GetFileSystemController();
167
164 // Attempt to load shared font data from disk 168 // Attempt to load shared font data from disk
165 const auto* nand = fsc.GetSystemNANDContents(); 169 const auto* nand = fsc.GetSystemNANDContents();
166 std::size_t offset = 0; 170 std::size_t offset = 0;
@@ -325,7 +329,7 @@ void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) {
325 Kernel::MemoryState::Shared); 329 Kernel::MemoryState::Shared);
326 330
327 // Create shared font memory object 331 // Create shared font memory object
328 auto& kernel = Core::System::GetInstance().Kernel(); 332 auto& kernel = system.Kernel();
329 impl->shared_font_mem = Kernel::SharedMemory::Create( 333 impl->shared_font_mem = Kernel::SharedMemory::Create(
330 kernel, Core::CurrentProcess(), SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite, 334 kernel, Core::CurrentProcess(), SHARED_FONT_MEM_SIZE, Kernel::MemoryPermission::ReadWrite,
331 Kernel::MemoryPermission::Read, SHARED_FONT_MEM_VADDR, Kernel::MemoryRegion::BASE, 335 Kernel::MemoryPermission::Read, SHARED_FONT_MEM_VADDR, Kernel::MemoryRegion::BASE,
diff --git a/src/core/hle/service/ns/pl_u.h b/src/core/hle/service/ns/pl_u.h
index 35ca424d2..1063f4204 100644
--- a/src/core/hle/service/ns/pl_u.h
+++ b/src/core/hle/service/ns/pl_u.h
@@ -17,7 +17,7 @@ namespace NS {
17 17
18class PL_U final : public ServiceFramework<PL_U> { 18class PL_U final : public ServiceFramework<PL_U> {
19public: 19public:
20 PL_U(FileSystem::FileSystemController& fsc); 20 explicit PL_U(Core::System& system);
21 ~PL_U() override; 21 ~PL_U() override;
22 22
23private: 23private:
@@ -30,6 +30,7 @@ private:
30 30
31 struct Impl; 31 struct Impl;
32 std::unique_ptr<Impl> impl; 32 std::unique_ptr<Impl> impl;
33 Core::System& system;
33}; 34};
34 35
35} // namespace NS 36} // namespace NS
diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h
index 5b8248433..1b52511a5 100644
--- a/src/core/hle/service/nvdrv/devices/nvdevice.h
+++ b/src/core/hle/service/nvdrv/devices/nvdevice.h
@@ -9,6 +9,7 @@
9#include "common/common_types.h" 9#include "common/common_types.h"
10#include "common/swap.h" 10#include "common/swap.h"
11#include "core/hle/service/nvdrv/nvdata.h" 11#include "core/hle/service/nvdrv/nvdata.h"
12#include "core/hle/service/service.h"
12 13
13namespace Core { 14namespace Core {
14class System; 15class System;
@@ -38,8 +39,9 @@ public:
38 * @param output A buffer where the output data will be written to. 39 * @param output A buffer where the output data will be written to.
39 * @returns The result code of the ioctl. 40 * @returns The result code of the ioctl.
40 */ 41 */
41 virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 42 virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
42 IoctlCtrl& ctrl) = 0; 43 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
44 IoctlVersion version) = 0;
43 45
44protected: 46protected:
45 Core::System& system; 47 Core::System& system;
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
index 926a1285d..f764388bc 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp
@@ -17,8 +17,9 @@ nvdisp_disp0::nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_de
17 : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} 17 : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
18nvdisp_disp0 ::~nvdisp_disp0() = default; 18nvdisp_disp0 ::~nvdisp_disp0() = default;
19 19
20u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 20u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
21 IoctlCtrl& ctrl) { 21 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
22 IoctlVersion version) {
22 UNIMPLEMENTED_MSG("Unimplemented ioctl"); 23 UNIMPLEMENTED_MSG("Unimplemented ioctl");
23 return 0; 24 return 0;
24} 25}
diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
index e79e490ff..6fcdeee84 100644
--- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
+++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h
@@ -20,8 +20,9 @@ public:
20 explicit nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); 20 explicit nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
21 ~nvdisp_disp0() override; 21 ~nvdisp_disp0() override;
22 22
23 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 23 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
24 IoctlCtrl& ctrl) override; 24 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
25 IoctlVersion version) override;
25 26
26 /// Performs a screen flip, drawing the buffer pointed to by the handle. 27 /// Performs a screen flip, drawing the buffer pointed to by the handle.
27 void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride, 28 void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride,
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 24ab3f2e9..6bc053f27 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -26,8 +26,9 @@ nvhost_as_gpu::nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_
26 : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} 26 : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
27nvhost_as_gpu::~nvhost_as_gpu() = default; 27nvhost_as_gpu::~nvhost_as_gpu() = default;
28 28
29u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 29u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
30 IoctlCtrl& ctrl) { 30 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
31 IoctlVersion version) {
31 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 32 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
32 command.raw, input.size(), output.size()); 33 command.raw, input.size(), output.size());
33 34
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
index 30ca5f4c3..169fb8f0e 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h
@@ -20,8 +20,9 @@ public:
20 explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); 20 explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
21 ~nvhost_as_gpu() override; 21 ~nvhost_as_gpu() override;
22 22
23 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 23 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
24 IoctlCtrl& ctrl) override; 24 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
25 IoctlVersion version) override;
25 26
26private: 27private:
27 enum class IoctlCommand : u32_le { 28 enum class IoctlCommand : u32_le {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
index 9a66a5f88..ff6b1abae 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -19,8 +19,9 @@ nvhost_ctrl::nvhost_ctrl(Core::System& system, EventInterface& events_interface)
19 : nvdevice(system), events_interface{events_interface} {} 19 : nvdevice(system), events_interface{events_interface} {}
20nvhost_ctrl::~nvhost_ctrl() = default; 20nvhost_ctrl::~nvhost_ctrl() = default;
21 21
22u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 22u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
23 IoctlCtrl& ctrl) { 23 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
24 IoctlVersion version) {
24 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 25 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
25 command.raw, input.size(), output.size()); 26 command.raw, input.size(), output.size());
26 27
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
index 14e6e7e57..9898623de 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
@@ -17,8 +17,9 @@ public:
17 explicit nvhost_ctrl(Core::System& system, EventInterface& events_interface); 17 explicit nvhost_ctrl(Core::System& system, EventInterface& events_interface);
18 ~nvhost_ctrl() override; 18 ~nvhost_ctrl() override;
19 19
20 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 20 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
21 IoctlCtrl& ctrl) override; 21 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
22 IoctlVersion version) override;
22 23
23private: 24private:
24 enum class IoctlCommand : u32_le { 25 enum class IoctlCommand : u32_le {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
index 988effd90..389ace76f 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -15,14 +15,15 @@ namespace Service::Nvidia::Devices {
15nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system) : nvdevice(system) {} 15nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system) : nvdevice(system) {}
16nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default; 16nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default;
17 17
18u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 18u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input,
19 IoctlCtrl& ctrl) { 19 const std::vector<u8>& input2, std::vector<u8>& output,
20 std::vector<u8>& output2, IoctlCtrl& ctrl, IoctlVersion version) {
20 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 21 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
21 command.raw, input.size(), output.size()); 22 command.raw, input.size(), output.size());
22 23
23 switch (static_cast<IoctlCommand>(command.raw)) { 24 switch (static_cast<IoctlCommand>(command.raw)) {
24 case IoctlCommand::IocGetCharacteristicsCommand: 25 case IoctlCommand::IocGetCharacteristicsCommand:
25 return GetCharacteristics(input, output); 26 return GetCharacteristics(input, output, output2, version);
26 case IoctlCommand::IocGetTPCMasksCommand: 27 case IoctlCommand::IocGetTPCMasksCommand:
27 return GetTPCMasks(input, output); 28 return GetTPCMasks(input, output);
28 case IoctlCommand::IocGetActiveSlotMaskCommand: 29 case IoctlCommand::IocGetActiveSlotMaskCommand:
@@ -44,7 +45,8 @@ u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vec
44 return 0; 45 return 0;
45} 46}
46 47
47u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output) { 48u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output,
49 std::vector<u8>& output2, IoctlVersion version) {
48 LOG_DEBUG(Service_NVDRV, "called"); 50 LOG_DEBUG(Service_NVDRV, "called");
49 IoctlCharacteristics params{}; 51 IoctlCharacteristics params{};
50 std::memcpy(&params, input.data(), input.size()); 52 std::memcpy(&params, input.data(), input.size());
@@ -85,7 +87,13 @@ u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vecto
85 params.gc.gr_compbit_store_base_hw = 0x0; 87 params.gc.gr_compbit_store_base_hw = 0x0;
86 params.gpu_characteristics_buf_size = 0xA0; 88 params.gpu_characteristics_buf_size = 0xA0;
87 params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED) 89 params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED)
88 std::memcpy(output.data(), &params, output.size()); 90
91 if (version == IoctlVersion::Version3) {
92 std::memcpy(output.data(), input.data(), output.size());
93 std::memcpy(output2.data(), &params.gc, output2.size());
94 } else {
95 std::memcpy(output.data(), &params, output.size());
96 }
89 return 0; 97 return 0;
90} 98}
91 99
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
index 2b035ae3f..642b0a2cb 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
@@ -16,8 +16,9 @@ public:
16 explicit nvhost_ctrl_gpu(Core::System& system); 16 explicit nvhost_ctrl_gpu(Core::System& system);
17 ~nvhost_ctrl_gpu() override; 17 ~nvhost_ctrl_gpu() override;
18 18
19 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 19 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
20 IoctlCtrl& ctrl) override; 20 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
21 IoctlVersion version) override;
21 22
22private: 23private:
23 enum class IoctlCommand : u32_le { 24 enum class IoctlCommand : u32_le {
@@ -162,7 +163,8 @@ private:
162 }; 163 };
163 static_assert(sizeof(IoctlGetGpuTime) == 8, "IoctlGetGpuTime is incorrect size"); 164 static_assert(sizeof(IoctlGetGpuTime) == 8, "IoctlGetGpuTime is incorrect size");
164 165
165 u32 GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output); 166 u32 GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output,
167 std::vector<u8>& output2, IoctlVersion version);
166 u32 GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output); 168 u32 GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output);
167 u32 GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output); 169 u32 GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output);
168 u32 ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output); 170 u32 ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output);
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index b4ee2a255..2b8d1bef6 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -17,8 +17,9 @@ nvhost_gpu::nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
17 : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} 17 : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
18nvhost_gpu::~nvhost_gpu() = default; 18nvhost_gpu::~nvhost_gpu() = default;
19 19
20u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 20u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
21 IoctlCtrl& ctrl) { 21 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
22 IoctlVersion version) {
22 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 23 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
23 command.raw, input.size(), output.size()); 24 command.raw, input.size(), output.size());
24 25
@@ -50,7 +51,7 @@ u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u
50 return SubmitGPFIFO(input, output); 51 return SubmitGPFIFO(input, output);
51 } 52 }
52 if (command.cmd == NVGPU_IOCTL_CHANNEL_KICKOFF_PB) { 53 if (command.cmd == NVGPU_IOCTL_CHANNEL_KICKOFF_PB) {
53 return KickoffPB(input, output); 54 return KickoffPB(input, output, input2, version);
54 } 55 }
55 } 56 }
56 57
@@ -173,7 +174,8 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp
173 return 0; 174 return 0;
174} 175}
175 176
176u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output) { 177u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output,
178 const std::vector<u8>& input2, IoctlVersion version) {
177 if (input.size() < sizeof(IoctlSubmitGpfifo)) { 179 if (input.size() < sizeof(IoctlSubmitGpfifo)) {
178 UNIMPLEMENTED(); 180 UNIMPLEMENTED();
179 } 181 }
@@ -183,9 +185,13 @@ u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output)
183 params.num_entries, params.flags.raw); 185 params.num_entries, params.flags.raw);
184 186
185 Tegra::CommandList entries(params.num_entries); 187 Tegra::CommandList entries(params.num_entries);
186 Memory::ReadBlock(params.address, entries.data(), 188 if (version == IoctlVersion::Version2) {
187 params.num_entries * sizeof(Tegra::CommandListHeader)); 189 std::memcpy(entries.data(), input2.data(),
188 190 params.num_entries * sizeof(Tegra::CommandListHeader));
191 } else {
192 Memory::ReadBlock(params.address, entries.data(),
193 params.num_entries * sizeof(Tegra::CommandListHeader));
194 }
189 UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0); 195 UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0);
190 UNIMPLEMENTED_IF(params.flags.add_increment.Value() != 0); 196 UNIMPLEMENTED_IF(params.flags.add_increment.Value() != 0);
191 197
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
index d2e8fbae9..d056dd046 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
@@ -24,8 +24,9 @@ public:
24 explicit nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); 24 explicit nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
25 ~nvhost_gpu() override; 25 ~nvhost_gpu() override;
26 26
27 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 27 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
28 IoctlCtrl& ctrl) override; 28 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
29 IoctlVersion version) override;
29 30
30private: 31private:
31 enum class IoctlCommand : u32_le { 32 enum class IoctlCommand : u32_le {
@@ -183,7 +184,8 @@ private:
183 u32 AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output); 184 u32 AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output);
184 u32 AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output); 185 u32 AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output);
185 u32 SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output); 186 u32 SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output);
186 u32 KickoffPB(const std::vector<u8>& input, std::vector<u8>& output); 187 u32 KickoffPB(const std::vector<u8>& input, std::vector<u8>& output,
188 const std::vector<u8>& input2, IoctlVersion version);
187 u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output); 189 u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
188 u32 ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output); 190 u32 ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output);
189 191
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
index f572ad30f..bdae8b887 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
@@ -13,8 +13,9 @@ namespace Service::Nvidia::Devices {
13nvhost_nvdec::nvhost_nvdec(Core::System& system) : nvdevice(system) {} 13nvhost_nvdec::nvhost_nvdec(Core::System& system) : nvdevice(system) {}
14nvhost_nvdec::~nvhost_nvdec() = default; 14nvhost_nvdec::~nvhost_nvdec() = default;
15 15
16u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 16u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
17 IoctlCtrl& ctrl) { 17 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
18 IoctlVersion version) {
18 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 19 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
19 command.raw, input.size(), output.size()); 20 command.raw, input.size(), output.size());
20 21
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
index 2710f0511..cbdac8069 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
@@ -16,8 +16,9 @@ public:
16 explicit nvhost_nvdec(Core::System& system); 16 explicit nvhost_nvdec(Core::System& system);
17 ~nvhost_nvdec() override; 17 ~nvhost_nvdec() override;
18 18
19 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 19 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
20 IoctlCtrl& ctrl) override; 20 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
21 IoctlVersion version) override;
21 22
22private: 23private:
23 enum class IoctlCommand : u32_le { 24 enum class IoctlCommand : u32_le {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
index 38282956f..96e7b7dab 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
@@ -13,8 +13,9 @@ namespace Service::Nvidia::Devices {
13nvhost_nvjpg::nvhost_nvjpg(Core::System& system) : nvdevice(system) {} 13nvhost_nvjpg::nvhost_nvjpg(Core::System& system) : nvdevice(system) {}
14nvhost_nvjpg::~nvhost_nvjpg() = default; 14nvhost_nvjpg::~nvhost_nvjpg() = default;
15 15
16u32 nvhost_nvjpg::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 16u32 nvhost_nvjpg::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
17 IoctlCtrl& ctrl) { 17 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
18 IoctlVersion version) {
18 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 19 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
19 command.raw, input.size(), output.size()); 20 command.raw, input.size(), output.size());
20 21
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
index 379766693..98dcac52f 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h
@@ -16,8 +16,9 @@ public:
16 explicit nvhost_nvjpg(Core::System& system); 16 explicit nvhost_nvjpg(Core::System& system);
17 ~nvhost_nvjpg() override; 17 ~nvhost_nvjpg() override;
18 18
19 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 19 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
20 IoctlCtrl& ctrl) override; 20 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
21 IoctlVersion version) override;
21 22
22private: 23private:
23 enum class IoctlCommand : u32_le { 24 enum class IoctlCommand : u32_le {
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
index 70e8091db..c695b8863 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
@@ -13,8 +13,9 @@ namespace Service::Nvidia::Devices {
13nvhost_vic::nvhost_vic(Core::System& system) : nvdevice(system) {} 13nvhost_vic::nvhost_vic(Core::System& system) : nvdevice(system) {}
14nvhost_vic::~nvhost_vic() = default; 14nvhost_vic::~nvhost_vic() = default;
15 15
16u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 16u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
17 IoctlCtrl& ctrl) { 17 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
18 IoctlVersion version) {
18 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", 19 LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
19 command.raw, input.size(), output.size()); 20 command.raw, input.size(), output.size());
20 21
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
index 7d111977e..bec32bea1 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
@@ -16,8 +16,9 @@ public:
16 explicit nvhost_vic(Core::System& system); 16 explicit nvhost_vic(Core::System& system);
17 ~nvhost_vic() override; 17 ~nvhost_vic() override;
18 18
19 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 19 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
20 IoctlCtrl& ctrl) override; 20 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
21 IoctlVersion version) override;
21 22
22private: 23private:
23 enum class IoctlCommand : u32_le { 24 enum class IoctlCommand : u32_le {
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp
index 223b496b7..8c742316c 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp
@@ -28,8 +28,9 @@ VAddr nvmap::GetObjectAddress(u32 handle) const {
28 return object->addr; 28 return object->addr;
29} 29}
30 30
31u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 31u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
32 IoctlCtrl& ctrl) { 32 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
33 IoctlVersion version) {
33 switch (static_cast<IoctlCommand>(command.raw)) { 34 switch (static_cast<IoctlCommand>(command.raw)) {
34 case IoctlCommand::Create: 35 case IoctlCommand::Create:
35 return IocCreate(input, output); 36 return IocCreate(input, output);
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h
index bf4a101c2..73c2e8809 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.h
+++ b/src/core/hle/service/nvdrv/devices/nvmap.h
@@ -22,8 +22,9 @@ public:
22 /// Returns the allocated address of an nvmap object given its handle. 22 /// Returns the allocated address of an nvmap object given its handle.
23 VAddr GetObjectAddress(u32 handle) const; 23 VAddr GetObjectAddress(u32 handle) const;
24 24
25 u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, 25 u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
26 IoctlCtrl& ctrl) override; 26 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
27 IoctlVersion version) override;
27 28
28 /// Represents an nvmap object. 29 /// Represents an nvmap object.
29 struct Object { 30 struct Object {
diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp
index d5be64ed2..5e0c23602 100644
--- a/src/core/hle/service/nvdrv/interface.cpp
+++ b/src/core/hle/service/nvdrv/interface.cpp
@@ -33,42 +33,77 @@ void NVDRV::Open(Kernel::HLERequestContext& ctx) {
33 rb.Push<u32>(0); 33 rb.Push<u32>(0);
34} 34}
35 35
36void NVDRV::Ioctl(Kernel::HLERequestContext& ctx) { 36void NVDRV::IoctlBase(Kernel::HLERequestContext& ctx, IoctlVersion version) {
37 LOG_DEBUG(Service_NVDRV, "called");
38
39 IPC::RequestParser rp{ctx}; 37 IPC::RequestParser rp{ctx};
40 u32 fd = rp.Pop<u32>(); 38 u32 fd = rp.Pop<u32>();
41 u32 command = rp.Pop<u32>(); 39 u32 command = rp.Pop<u32>();
42 40
43 std::vector<u8> output(ctx.GetWriteBufferSize()); 41 /// Ioctl 3 has 2 outputs, first in the input params, second is the result
42 std::vector<u8> output(ctx.GetWriteBufferSize(0));
43 std::vector<u8> output2;
44 if (version == IoctlVersion::Version3) {
45 output2.resize((ctx.GetWriteBufferSize(1)));
46 }
47
48 /// Ioctl2 has 2 inputs. It's used to pass data directly instead of providing a pointer.
49 /// KickOfPB uses this
50 auto input = ctx.ReadBuffer(0);
51
52 std::vector<u8> input2;
53 if (version == IoctlVersion::Version2) {
54 input2 = ctx.ReadBuffer(1);
55 }
44 56
45 IoctlCtrl ctrl{}; 57 IoctlCtrl ctrl{};
46 58
47 u32 result = nvdrv->Ioctl(fd, command, ctx.ReadBuffer(), output, ctrl); 59 u32 result = nvdrv->Ioctl(fd, command, input, input2, output, output2, ctrl, version);
48 60
49 if (ctrl.must_delay) { 61 if (ctrl.must_delay) {
50 ctrl.fresh_call = false; 62 ctrl.fresh_call = false;
51 ctx.SleepClientThread( 63 ctx.SleepClientThread("NVServices::DelayedResponse", ctrl.timeout,
52 "NVServices::DelayedResponse", ctrl.timeout, 64 [=](Kernel::SharedPtr<Kernel::Thread> thread,
53 [=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx, 65 Kernel::HLERequestContext& ctx,
54 Kernel::ThreadWakeupReason reason) { 66 Kernel::ThreadWakeupReason reason) {
55 IoctlCtrl ctrl2{ctrl}; 67 IoctlCtrl ctrl2{ctrl};
56 std::vector<u8> output2 = output; 68 std::vector<u8> tmp_output = output;
57 u32 result = nvdrv->Ioctl(fd, command, ctx.ReadBuffer(), output2, ctrl2); 69 std::vector<u8> tmp_output2 = output2;
58 ctx.WriteBuffer(output2); 70 u32 result = nvdrv->Ioctl(fd, command, input, input2, tmp_output,
59 IPC::ResponseBuilder rb{ctx, 3}; 71 tmp_output2, ctrl2, version);
60 rb.Push(RESULT_SUCCESS); 72 ctx.WriteBuffer(tmp_output, 0);
61 rb.Push(result); 73 if (version == IoctlVersion::Version3) {
62 }, 74 ctx.WriteBuffer(tmp_output2, 1);
63 nvdrv->GetEventWriteable(ctrl.event_id)); 75 }
76 IPC::ResponseBuilder rb{ctx, 3};
77 rb.Push(RESULT_SUCCESS);
78 rb.Push(result);
79 },
80 nvdrv->GetEventWriteable(ctrl.event_id));
64 } else { 81 } else {
65 ctx.WriteBuffer(output); 82 ctx.WriteBuffer(output);
83 if (version == IoctlVersion::Version3) {
84 ctx.WriteBuffer(output2, 1);
85 }
66 } 86 }
67 IPC::ResponseBuilder rb{ctx, 3}; 87 IPC::ResponseBuilder rb{ctx, 3};
68 rb.Push(RESULT_SUCCESS); 88 rb.Push(RESULT_SUCCESS);
69 rb.Push(result); 89 rb.Push(result);
70} 90}
71 91
92void NVDRV::Ioctl(Kernel::HLERequestContext& ctx) {
93 LOG_DEBUG(Service_NVDRV, "called");
94 IoctlBase(ctx, IoctlVersion::Version1);
95}
96
97void NVDRV::Ioctl2(Kernel::HLERequestContext& ctx) {
98 LOG_DEBUG(Service_NVDRV, "called");
99 IoctlBase(ctx, IoctlVersion::Version2);
100}
101
102void NVDRV::Ioctl3(Kernel::HLERequestContext& ctx) {
103 LOG_DEBUG(Service_NVDRV, "called");
104 IoctlBase(ctx, IoctlVersion::Version3);
105}
106
72void NVDRV::Close(Kernel::HLERequestContext& ctx) { 107void NVDRV::Close(Kernel::HLERequestContext& ctx) {
73 LOG_DEBUG(Service_NVDRV, "called"); 108 LOG_DEBUG(Service_NVDRV, "called");
74 109
@@ -154,8 +189,8 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name)
154 {8, &NVDRV::SetClientPID, "SetClientPID"}, 189 {8, &NVDRV::SetClientPID, "SetClientPID"},
155 {9, &NVDRV::DumpGraphicsMemoryInfo, "DumpGraphicsMemoryInfo"}, 190 {9, &NVDRV::DumpGraphicsMemoryInfo, "DumpGraphicsMemoryInfo"},
156 {10, nullptr, "InitializeDevtools"}, 191 {10, nullptr, "InitializeDevtools"},
157 {11, &NVDRV::Ioctl, "Ioctl2"}, 192 {11, &NVDRV::Ioctl2, "Ioctl2"},
158 {12, nullptr, "Ioctl3"}, 193 {12, &NVDRV::Ioctl3, "Ioctl3"},
159 {13, &NVDRV::FinishInitialize, "FinishInitialize"}, 194 {13, &NVDRV::FinishInitialize, "FinishInitialize"},
160 }; 195 };
161 RegisterHandlers(functions); 196 RegisterHandlers(functions);
diff --git a/src/core/hle/service/nvdrv/interface.h b/src/core/hle/service/nvdrv/interface.h
index 10a0ecd52..9269ce00c 100644
--- a/src/core/hle/service/nvdrv/interface.h
+++ b/src/core/hle/service/nvdrv/interface.h
@@ -24,6 +24,8 @@ public:
24private: 24private:
25 void Open(Kernel::HLERequestContext& ctx); 25 void Open(Kernel::HLERequestContext& ctx);
26 void Ioctl(Kernel::HLERequestContext& ctx); 26 void Ioctl(Kernel::HLERequestContext& ctx);
27 void Ioctl2(Kernel::HLERequestContext& ctx);
28 void Ioctl3(Kernel::HLERequestContext& ctx);
27 void Close(Kernel::HLERequestContext& ctx); 29 void Close(Kernel::HLERequestContext& ctx);
28 void Initialize(Kernel::HLERequestContext& ctx); 30 void Initialize(Kernel::HLERequestContext& ctx);
29 void QueryEvent(Kernel::HLERequestContext& ctx); 31 void QueryEvent(Kernel::HLERequestContext& ctx);
@@ -31,6 +33,7 @@ private:
31 void FinishInitialize(Kernel::HLERequestContext& ctx); 33 void FinishInitialize(Kernel::HLERequestContext& ctx);
32 void GetStatus(Kernel::HLERequestContext& ctx); 34 void GetStatus(Kernel::HLERequestContext& ctx);
33 void DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx); 35 void DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx);
36 void IoctlBase(Kernel::HLERequestContext& ctx, IoctlVersion version);
34 37
35 std::shared_ptr<Module> nvdrv; 38 std::shared_ptr<Module> nvdrv;
36 39
diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h
index ac03cbc23..529b03471 100644
--- a/src/core/hle/service/nvdrv/nvdata.h
+++ b/src/core/hle/service/nvdrv/nvdata.h
@@ -34,6 +34,12 @@ enum class EventState {
34 Busy = 3, 34 Busy = 3,
35}; 35};
36 36
37enum class IoctlVersion : u32 {
38 Version1,
39 Version2,
40 Version3,
41};
42
37struct IoctlCtrl { 43struct IoctlCtrl {
38 // First call done to the servioce for services that call itself again after a call. 44 // First call done to the servioce for services that call itself again after a call.
39 bool fresh_call{true}; 45 bool fresh_call{true};
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index 2011a226a..307a7e928 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -71,13 +71,14 @@ u32 Module::Open(const std::string& device_name) {
71 return fd; 71 return fd;
72} 72}
73 73
74u32 Module::Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output, 74u32 Module::Ioctl(u32 fd, u32 command, const std::vector<u8>& input, const std::vector<u8>& input2,
75 IoctlCtrl& ctrl) { 75 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
76 IoctlVersion version) {
76 auto itr = open_files.find(fd); 77 auto itr = open_files.find(fd);
77 ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device"); 78 ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device");
78 79
79 auto& device = itr->second; 80 auto& device = itr->second;
80 return device->ioctl({command}, input, output, ctrl); 81 return device->ioctl({command}, input, input2, output, output2, ctrl, version);
81} 82}
82 83
83ResultCode Module::Close(u32 fd) { 84ResultCode Module::Close(u32 fd) {
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index a339ab672..f8bb28969 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -106,8 +106,9 @@ public:
106 /// Opens a device node and returns a file descriptor to it. 106 /// Opens a device node and returns a file descriptor to it.
107 u32 Open(const std::string& device_name); 107 u32 Open(const std::string& device_name);
108 /// Sends an ioctl command to the specified file descriptor. 108 /// Sends an ioctl command to the specified file descriptor.
109 u32 Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output, 109 u32 Ioctl(u32 fd, u32 command, const std::vector<u8>& input, const std::vector<u8>& input2,
110 IoctlCtrl& ctrl); 110 std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
111 IoctlVersion version);
111 /// Closes a device file descriptor and returns operation success. 112 /// Closes a device file descriptor and returns operation success.
112 ResultCode Close(u32 fd); 113 ResultCode Close(u32 fd);
113 114
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index f9db79370..2e4d707b9 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -29,26 +29,28 @@ namespace Service::NVFlinger {
29constexpr s64 frame_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 60); 29constexpr s64 frame_ticks = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 60);
30constexpr s64 frame_ticks_30fps = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 30); 30constexpr s64 frame_ticks_30fps = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 30);
31 31
32NVFlinger::NVFlinger(Core::Timing::CoreTiming& core_timing) : core_timing{core_timing} { 32NVFlinger::NVFlinger(Core::System& system) : system(system) {
33 displays.emplace_back(0, "Default"); 33 displays.emplace_back(0, "Default", system);
34 displays.emplace_back(1, "External"); 34 displays.emplace_back(1, "External", system);
35 displays.emplace_back(2, "Edid"); 35 displays.emplace_back(2, "Edid", system);
36 displays.emplace_back(3, "Internal"); 36 displays.emplace_back(3, "Internal", system);
37 displays.emplace_back(4, "Null"); 37 displays.emplace_back(4, "Null", system);
38 38
39 // Schedule the screen composition events 39 // Schedule the screen composition events
40 composition_event = core_timing.RegisterEvent("ScreenComposition", [this](u64 userdata, 40 composition_event = system.CoreTiming().RegisterEvent(
41 s64 cycles_late) { 41 "ScreenComposition", [this](u64 userdata, s64 cycles_late) {
42 Compose(); 42 Compose();
43 const auto ticks = Settings::values.force_30fps_mode ? frame_ticks_30fps : GetNextTicks(); 43 const auto ticks =
44 this->core_timing.ScheduleEvent(std::max<s64>(0LL, ticks - cycles_late), composition_event); 44 Settings::values.force_30fps_mode ? frame_ticks_30fps : GetNextTicks();
45 }); 45 this->system.CoreTiming().ScheduleEvent(std::max<s64>(0LL, ticks - cycles_late),
46 46 composition_event);
47 core_timing.ScheduleEvent(frame_ticks, composition_event); 47 });
48
49 system.CoreTiming().ScheduleEvent(frame_ticks, composition_event);
48} 50}
49 51
50NVFlinger::~NVFlinger() { 52NVFlinger::~NVFlinger() {
51 core_timing.UnscheduleEvent(composition_event, 0); 53 system.CoreTiming().UnscheduleEvent(composition_event, 0);
52} 54}
53 55
54void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) { 56void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
@@ -185,11 +187,9 @@ void NVFlinger::Compose() {
185 MicroProfileFlip(); 187 MicroProfileFlip();
186 188
187 if (!buffer) { 189 if (!buffer) {
188 auto& system_instance = Core::System::GetInstance();
189
190 // There was no queued buffer to draw, render previous frame 190 // There was no queued buffer to draw, render previous frame
191 system_instance.GetPerfStats().EndGameFrame(); 191 system.GetPerfStats().EndGameFrame();
192 system_instance.GPU().SwapBuffers({}); 192 system.GPU().SwapBuffers({});
193 continue; 193 continue;
194 } 194 }
195 195
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index 988be8726..5d7e3bfb8 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -38,7 +38,7 @@ class BufferQueue;
38 38
39class NVFlinger final { 39class NVFlinger final {
40public: 40public:
41 explicit NVFlinger(Core::Timing::CoreTiming& core_timing); 41 explicit NVFlinger(Core::System& system);
42 ~NVFlinger(); 42 ~NVFlinger();
43 43
44 /// Sets the NVDrv module instance to use to send buffers to the GPU. 44 /// Sets the NVDrv module instance to use to send buffers to the GPU.
@@ -105,8 +105,7 @@ private:
105 /// Event that handles screen composition. 105 /// Event that handles screen composition.
106 Core::Timing::EventType* composition_event; 106 Core::Timing::EventType* composition_event;
107 107
108 /// Core timing instance for registering/unregistering the composition event. 108 Core::System& system;
109 Core::Timing::CoreTiming& core_timing;
110}; 109};
111 110
112} // namespace Service::NVFlinger 111} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/prepo/prepo.cpp b/src/core/hle/service/prepo/prepo.cpp
index 0f79135ff..18d895263 100644
--- a/src/core/hle/service/prepo/prepo.cpp
+++ b/src/core/hle/service/prepo/prepo.cpp
@@ -15,7 +15,7 @@ namespace Service::PlayReport {
15 15
16class PlayReport final : public ServiceFramework<PlayReport> { 16class PlayReport final : public ServiceFramework<PlayReport> {
17public: 17public:
18 explicit PlayReport(Core::System& system, const char* name) 18 explicit PlayReport(const char* name, Core::System& system)
19 : ServiceFramework{name}, system(system) { 19 : ServiceFramework{name}, system(system) {
20 // clang-format off 20 // clang-format off
21 static const FunctionInfo functions[] = { 21 static const FunctionInfo functions[] = {
@@ -128,12 +128,12 @@ private:
128 Core::System& system; 128 Core::System& system;
129}; 129};
130 130
131void InstallInterfaces(Core::System& system) { 131void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
132 std::make_shared<PlayReport>(system, "prepo:a")->InstallAsService(system.ServiceManager()); 132 std::make_shared<PlayReport>("prepo:a", system)->InstallAsService(service_manager);
133 std::make_shared<PlayReport>(system, "prepo:a2")->InstallAsService(system.ServiceManager()); 133 std::make_shared<PlayReport>("prepo:a2", system)->InstallAsService(service_manager);
134 std::make_shared<PlayReport>(system, "prepo:m")->InstallAsService(system.ServiceManager()); 134 std::make_shared<PlayReport>("prepo:m", system)->InstallAsService(service_manager);
135 std::make_shared<PlayReport>(system, "prepo:s")->InstallAsService(system.ServiceManager()); 135 std::make_shared<PlayReport>("prepo:s", system)->InstallAsService(service_manager);
136 std::make_shared<PlayReport>(system, "prepo:u")->InstallAsService(system.ServiceManager()); 136 std::make_shared<PlayReport>("prepo:u", system)->InstallAsService(service_manager);
137} 137}
138 138
139} // namespace Service::PlayReport 139} // namespace Service::PlayReport
diff --git a/src/core/hle/service/prepo/prepo.h b/src/core/hle/service/prepo/prepo.h
index 0ebc3a938..a5682ee26 100644
--- a/src/core/hle/service/prepo/prepo.h
+++ b/src/core/hle/service/prepo/prepo.h
@@ -8,8 +8,12 @@ namespace Service::SM {
8class ServiceManager; 8class ServiceManager;
9} 9}
10 10
11namespace Core {
12class System;
13}
14
11namespace Service::PlayReport { 15namespace Service::PlayReport {
12 16
13void InstallInterfaces(Core::System& system); 17void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
14 18
15} // namespace Service::PlayReport 19} // namespace Service::PlayReport
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 906fdc415..f2c6fe9dc 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -198,50 +198,50 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co
198void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) { 198void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) {
199 // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it 199 // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
200 // here and pass it into the respective InstallInterfaces functions. 200 // here and pass it into the respective InstallInterfaces functions.
201 auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(system.CoreTiming()); 201 auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(system);
202 system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false); 202 system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false);
203 203
204 SM::ServiceManager::InstallInterfaces(sm); 204 SM::ServiceManager::InstallInterfaces(sm);
205 205
206 Account::InstallInterfaces(system); 206 Account::InstallInterfaces(system);
207 AM::InstallInterfaces(*sm, nv_flinger, system); 207 AM::InstallInterfaces(*sm, nv_flinger, system);
208 AOC::InstallInterfaces(*sm); 208 AOC::InstallInterfaces(*sm, system);
209 APM::InstallInterfaces(system); 209 APM::InstallInterfaces(system);
210 Audio::InstallInterfaces(*sm, system); 210 Audio::InstallInterfaces(*sm, system);
211 BCAT::InstallInterfaces(*sm); 211 BCAT::InstallInterfaces(system);
212 BPC::InstallInterfaces(*sm); 212 BPC::InstallInterfaces(*sm);
213 BtDrv::InstallInterfaces(*sm); 213 BtDrv::InstallInterfaces(*sm, system);
214 BTM::InstallInterfaces(*sm); 214 BTM::InstallInterfaces(*sm, system);
215 Capture::InstallInterfaces(*sm); 215 Capture::InstallInterfaces(*sm);
216 ERPT::InstallInterfaces(*sm); 216 ERPT::InstallInterfaces(*sm);
217 ES::InstallInterfaces(*sm); 217 ES::InstallInterfaces(*sm);
218 EUPLD::InstallInterfaces(*sm); 218 EUPLD::InstallInterfaces(*sm);
219 Fatal::InstallInterfaces(*sm); 219 Fatal::InstallInterfaces(*sm, system);
220 FGM::InstallInterfaces(*sm); 220 FGM::InstallInterfaces(*sm);
221 FileSystem::InstallInterfaces(system); 221 FileSystem::InstallInterfaces(system);
222 Friend::InstallInterfaces(*sm); 222 Friend::InstallInterfaces(*sm, system);
223 Glue::InstallInterfaces(system); 223 Glue::InstallInterfaces(system);
224 GRC::InstallInterfaces(*sm); 224 GRC::InstallInterfaces(*sm);
225 HID::InstallInterfaces(*sm); 225 HID::InstallInterfaces(*sm, system);
226 LBL::InstallInterfaces(*sm); 226 LBL::InstallInterfaces(*sm);
227 LDN::InstallInterfaces(*sm); 227 LDN::InstallInterfaces(*sm);
228 LDR::InstallInterfaces(*sm); 228 LDR::InstallInterfaces(*sm, system);
229 LM::InstallInterfaces(*sm); 229 LM::InstallInterfaces(*sm);
230 Migration::InstallInterfaces(*sm); 230 Migration::InstallInterfaces(*sm);
231 Mii::InstallInterfaces(*sm); 231 Mii::InstallInterfaces(*sm);
232 MM::InstallInterfaces(*sm); 232 MM::InstallInterfaces(*sm);
233 NCM::InstallInterfaces(*sm); 233 NCM::InstallInterfaces(*sm);
234 NFC::InstallInterfaces(*sm); 234 NFC::InstallInterfaces(*sm);
235 NFP::InstallInterfaces(*sm); 235 NFP::InstallInterfaces(*sm, system);
236 NIFM::InstallInterfaces(*sm); 236 NIFM::InstallInterfaces(*sm, system);
237 NIM::InstallInterfaces(*sm); 237 NIM::InstallInterfaces(*sm, system);
238 NPNS::InstallInterfaces(*sm); 238 NPNS::InstallInterfaces(*sm);
239 NS::InstallInterfaces(*sm, system.GetFileSystemController()); 239 NS::InstallInterfaces(*sm, system);
240 Nvidia::InstallInterfaces(*sm, *nv_flinger, system); 240 Nvidia::InstallInterfaces(*sm, *nv_flinger, system);
241 PCIe::InstallInterfaces(*sm); 241 PCIe::InstallInterfaces(*sm);
242 PCTL::InstallInterfaces(*sm); 242 PCTL::InstallInterfaces(*sm);
243 PCV::InstallInterfaces(*sm); 243 PCV::InstallInterfaces(*sm);
244 PlayReport::InstallInterfaces(system); 244 PlayReport::InstallInterfaces(*sm, system);
245 PM::InstallInterfaces(system); 245 PM::InstallInterfaces(system);
246 PSC::InstallInterfaces(*sm); 246 PSC::InstallInterfaces(*sm);
247 PSM::InstallInterfaces(*sm); 247 PSM::InstallInterfaces(*sm);
diff --git a/src/core/hle/service/time/interface.cpp b/src/core/hle/service/time/interface.cpp
index 1030185e0..9565e7de5 100644
--- a/src/core/hle/service/time/interface.cpp
+++ b/src/core/hle/service/time/interface.cpp
@@ -7,8 +7,8 @@
7namespace Service::Time { 7namespace Service::Time {
8 8
9Time::Time(std::shared_ptr<Module> time, std::shared_ptr<SharedMemory> shared_memory, 9Time::Time(std::shared_ptr<Module> time, std::shared_ptr<SharedMemory> shared_memory,
10 const char* name) 10 Core::System& system, const char* name)
11 : Module::Interface(std::move(time), std::move(shared_memory), name) { 11 : Module::Interface(std::move(time), std::move(shared_memory), system, name) {
12 // clang-format off 12 // clang-format off
13 static const FunctionInfo functions[] = { 13 static const FunctionInfo functions[] = {
14 {0, &Time::GetStandardUserSystemClock, "GetStandardUserSystemClock"}, 14 {0, &Time::GetStandardUserSystemClock, "GetStandardUserSystemClock"},
diff --git a/src/core/hle/service/time/interface.h b/src/core/hle/service/time/interface.h
index bdf0883e2..5c63a07f4 100644
--- a/src/core/hle/service/time/interface.h
+++ b/src/core/hle/service/time/interface.h
@@ -13,7 +13,7 @@ class SharedMemory;
13class Time final : public Module::Interface { 13class Time final : public Module::Interface {
14public: 14public:
15 explicit Time(std::shared_ptr<Module> time, std::shared_ptr<SharedMemory> shared_memory, 15 explicit Time(std::shared_ptr<Module> time, std::shared_ptr<SharedMemory> shared_memory,
16 const char* name); 16 Core::System& system, const char* name);
17 ~Time() override; 17 ~Time() override;
18}; 18};
19 19
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
index ae6446204..1b9ab8401 100644
--- a/src/core/hle/service/time/time.cpp
+++ b/src/core/hle/service/time/time.cpp
@@ -126,8 +126,8 @@ private:
126 126
127class ISteadyClock final : public ServiceFramework<ISteadyClock> { 127class ISteadyClock final : public ServiceFramework<ISteadyClock> {
128public: 128public:
129 ISteadyClock(std::shared_ptr<SharedMemory> shared_memory) 129 ISteadyClock(std::shared_ptr<SharedMemory> shared_memory, Core::System& system)
130 : ServiceFramework("ISteadyClock"), shared_memory(shared_memory) { 130 : ServiceFramework("ISteadyClock"), shared_memory(shared_memory), system(system) {
131 static const FunctionInfo functions[] = { 131 static const FunctionInfo functions[] = {
132 {0, &ISteadyClock::GetCurrentTimePoint, "GetCurrentTimePoint"}, 132 {0, &ISteadyClock::GetCurrentTimePoint, "GetCurrentTimePoint"},
133 }; 133 };
@@ -150,12 +150,13 @@ private:
150 } 150 }
151 151
152 SteadyClockTimePoint GetCurrentTimePoint() const { 152 SteadyClockTimePoint GetCurrentTimePoint() const {
153 const auto& core_timing = Core::System::GetInstance().CoreTiming(); 153 const auto& core_timing = system.CoreTiming();
154 const auto ms = Core::Timing::CyclesToMs(core_timing.GetTicks()); 154 const auto ms = Core::Timing::CyclesToMs(core_timing.GetTicks());
155 return {static_cast<u64_le>(ms.count() / 1000), {}}; 155 return {static_cast<u64_le>(ms.count() / 1000), {}};
156 } 156 }
157 157
158 std::shared_ptr<SharedMemory> shared_memory; 158 std::shared_ptr<SharedMemory> shared_memory;
159 Core::System& system;
159}; 160};
160 161
161class ITimeZoneService final : public ServiceFramework<ITimeZoneService> { 162class ITimeZoneService final : public ServiceFramework<ITimeZoneService> {
@@ -290,7 +291,7 @@ void Module::Interface::GetStandardSteadyClock(Kernel::HLERequestContext& ctx) {
290 291
291 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 292 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
292 rb.Push(RESULT_SUCCESS); 293 rb.Push(RESULT_SUCCESS);
293 rb.PushIpcInterface<ISteadyClock>(shared_memory); 294 rb.PushIpcInterface<ISteadyClock>(shared_memory, system);
294} 295}
295 296
296void Module::Interface::GetTimeZoneService(Kernel::HLERequestContext& ctx) { 297void Module::Interface::GetTimeZoneService(Kernel::HLERequestContext& ctx) {
@@ -325,7 +326,7 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
325 return; 326 return;
326 } 327 }
327 328
328 const auto& core_timing = Core::System::GetInstance().CoreTiming(); 329 const auto& core_timing = system.CoreTiming();
329 const auto ms = Core::Timing::CyclesToMs(core_timing.GetTicks()); 330 const auto ms = Core::Timing::CyclesToMs(core_timing.GetTicks());
330 const SteadyClockTimePoint steady_clock_time_point{static_cast<u64_le>(ms.count() / 1000), {}}; 331 const SteadyClockTimePoint steady_clock_time_point{static_cast<u64_le>(ms.count() / 1000), {}};
331 332
@@ -407,8 +408,10 @@ void Module::Interface::SetStandardUserSystemClockAutomaticCorrectionEnabled(
407} 408}
408 409
409Module::Interface::Interface(std::shared_ptr<Module> time, 410Module::Interface::Interface(std::shared_ptr<Module> time,
410 std::shared_ptr<SharedMemory> shared_memory, const char* name) 411 std::shared_ptr<SharedMemory> shared_memory, Core::System& system,
411 : ServiceFramework(name), time(std::move(time)), shared_memory(std::move(shared_memory)) {} 412 const char* name)
413 : ServiceFramework(name), time(std::move(time)), shared_memory(std::move(shared_memory)),
414 system(system) {}
412 415
413Module::Interface::~Interface() = default; 416Module::Interface::~Interface() = default;
414 417
@@ -416,9 +419,11 @@ void InstallInterfaces(Core::System& system) {
416 auto time = std::make_shared<Module>(); 419 auto time = std::make_shared<Module>();
417 auto shared_mem = std::make_shared<SharedMemory>(system); 420 auto shared_mem = std::make_shared<SharedMemory>(system);
418 421
419 std::make_shared<Time>(time, shared_mem, "time:a")->InstallAsService(system.ServiceManager()); 422 std::make_shared<Time>(time, shared_mem, system, "time:a")
420 std::make_shared<Time>(time, shared_mem, "time:s")->InstallAsService(system.ServiceManager()); 423 ->InstallAsService(system.ServiceManager());
421 std::make_shared<Time>(std::move(time), shared_mem, "time:u") 424 std::make_shared<Time>(time, shared_mem, system, "time:s")
425 ->InstallAsService(system.ServiceManager());
426 std::make_shared<Time>(std::move(time), shared_mem, system, "time:u")
422 ->InstallAsService(system.ServiceManager()); 427 ->InstallAsService(system.ServiceManager());
423} 428}
424 429
diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h
index e0708f856..c32d32860 100644
--- a/src/core/hle/service/time/time.h
+++ b/src/core/hle/service/time/time.h
@@ -80,7 +80,8 @@ public:
80 class Interface : public ServiceFramework<Interface> { 80 class Interface : public ServiceFramework<Interface> {
81 public: 81 public:
82 explicit Interface(std::shared_ptr<Module> time, 82 explicit Interface(std::shared_ptr<Module> time,
83 std::shared_ptr<SharedMemory> shared_memory, const char* name); 83 std::shared_ptr<SharedMemory> shared_memory, Core::System& system,
84 const char* name);
84 ~Interface() override; 85 ~Interface() override;
85 86
86 void GetStandardUserSystemClock(Kernel::HLERequestContext& ctx); 87 void GetStandardUserSystemClock(Kernel::HLERequestContext& ctx);
@@ -97,6 +98,7 @@ public:
97 protected: 98 protected:
98 std::shared_ptr<Module> time; 99 std::shared_ptr<Module> time;
99 std::shared_ptr<SharedMemory> shared_memory; 100 std::shared_ptr<SharedMemory> shared_memory;
101 Core::System& system;
100 }; 102 };
101}; 103};
102 104
diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp
index a8d088305..006a6d9ff 100644
--- a/src/core/hle/service/vi/display/vi_display.cpp
+++ b/src/core/hle/service/vi/display/vi_display.cpp
@@ -15,8 +15,8 @@
15 15
16namespace Service::VI { 16namespace Service::VI {
17 17
18Display::Display(u64 id, std::string name) : id{id}, name{std::move(name)} { 18Display::Display(u64 id, std::string name, Core::System& system) : id{id}, name{std::move(name)} {
19 auto& kernel = Core::System::GetInstance().Kernel(); 19 auto& kernel = system.Kernel();
20 vsync_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual, 20 vsync_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
21 fmt::format("Display VSync Event {}", id)); 21 fmt::format("Display VSync Event {}", id));
22} 22}
diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h
index 2acd46ff8..f56b5badc 100644
--- a/src/core/hle/service/vi/display/vi_display.h
+++ b/src/core/hle/service/vi/display/vi_display.h
@@ -26,7 +26,7 @@ public:
26 /// @param id The unique ID for this display. 26 /// @param id The unique ID for this display.
27 /// @param name The name for this display. 27 /// @param name The name for this display.
28 /// 28 ///
29 Display(u64 id, std::string name); 29 Display(u64 id, std::string name, Core::System& system);
30 ~Display(); 30 ~Display();
31 31
32 Display(const Display&) = delete; 32 Display(const Display&) = delete;
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index e75c700ad..f629892ae 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -150,6 +150,7 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
150 // Apply cheats if they exist and the program has a valid title ID 150 // Apply cheats if they exist and the program has a valid title ID
151 if (pm) { 151 if (pm) {
152 auto& system = Core::System::GetInstance(); 152 auto& system = Core::System::GetInstance();
153 system.SetCurrentProcessBuildID(nso_header.build_id);
153 const auto cheats = pm->CreateCheatList(system, nso_header.build_id); 154 const auto cheats = pm->CreateCheatList(system, nso_header.build_id);
154 if (!cheats.empty()) { 155 if (!cheats.empty()) {
155 system.RegisterCheatList(cheats, nso_header.build_id, load_base, image_size); 156 system.RegisterCheatList(cheats, nso_header.build_id, load_base, image_size);
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index 7de3fd1e5..d1fc94060 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -103,6 +103,8 @@ void LogSettings() {
103 LogSetting("Debugging_UseGdbstub", Settings::values.use_gdbstub); 103 LogSetting("Debugging_UseGdbstub", Settings::values.use_gdbstub);
104 LogSetting("Debugging_GdbstubPort", Settings::values.gdbstub_port); 104 LogSetting("Debugging_GdbstubPort", Settings::values.gdbstub_port);
105 LogSetting("Debugging_ProgramArgs", Settings::values.program_args); 105 LogSetting("Debugging_ProgramArgs", Settings::values.program_args);
106 LogSetting("Services_BCATBackend", Settings::values.bcat_backend);
107 LogSetting("Services_BCATBoxcatLocal", Settings::values.bcat_boxcat_local);
106} 108}
107 109
108} // namespace Settings 110} // namespace Settings
diff --git a/src/core/settings.h b/src/core/settings.h
index 47bddfb30..9c98a9287 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -448,6 +448,10 @@ struct Values {
448 bool reporting_services; 448 bool reporting_services;
449 bool quest_flag; 449 bool quest_flag;
450 450
451 // BCAT
452 std::string bcat_backend;
453 bool bcat_boxcat_local;
454
451 // WebService 455 // WebService
452 bool enable_telemetry; 456 bool enable_telemetry;
453 std::string web_api_url; 457 std::string web_api_url;
diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp
index 98a8b5337..7ff44f06d 100644
--- a/src/video_core/engines/fermi_2d.cpp
+++ b/src/video_core/engines/fermi_2d.cpp
@@ -29,8 +29,8 @@ void Fermi2D::CallMethod(const GPU::MethodCall& method_call) {
29} 29}
30 30
31void Fermi2D::HandleSurfaceCopy() { 31void Fermi2D::HandleSurfaceCopy() {
32 LOG_WARNING(HW_GPU, "Requested a surface copy with operation {}", 32 LOG_DEBUG(HW_GPU, "Requested a surface copy with operation {}",
33 static_cast<u32>(regs.operation)); 33 static_cast<u32>(regs.operation));
34 34
35 // TODO(Subv): Only raw copies are implemented. 35 // TODO(Subv): Only raw copies are implemented.
36 ASSERT(regs.operation == Operation::SrcCopy); 36 ASSERT(regs.operation == Operation::SrcCopy);
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index fb3d1112c..b318aedb8 100644
--- a/src/video_core/engines/maxwell_3d.cpp
+++ b/src/video_core/engines/maxwell_3d.cpp
@@ -92,6 +92,10 @@ void Maxwell3D::InitializeRegisterDefaults() {
92 92
93 // Some games (like Super Mario Odyssey) assume that SRGB is enabled. 93 // Some games (like Super Mario Odyssey) assume that SRGB is enabled.
94 regs.framebuffer_srgb = 1; 94 regs.framebuffer_srgb = 1;
95 mme_inline[MAXWELL3D_REG_INDEX(draw.vertex_end_gl)] = true;
96 mme_inline[MAXWELL3D_REG_INDEX(draw.vertex_begin_gl)] = true;
97 mme_inline[MAXWELL3D_REG_INDEX(vertex_buffer.count)] = true;
98 mme_inline[MAXWELL3D_REG_INDEX(index_array.count)] = true;
95} 99}
96 100
97#define DIRTY_REGS_POS(field_name) (offsetof(Maxwell3D::DirtyRegs, field_name)) 101#define DIRTY_REGS_POS(field_name) (offsetof(Maxwell3D::DirtyRegs, field_name))
@@ -256,6 +260,9 @@ void Maxwell3D::CallMacroMethod(u32 method, std::size_t num_parameters, const u3
256 260
257 // Execute the current macro. 261 // Execute the current macro.
258 macro_interpreter.Execute(macro_positions[entry], num_parameters, parameters); 262 macro_interpreter.Execute(macro_positions[entry], num_parameters, parameters);
263 if (mme_draw.current_mode != MMEDrawMode::Undefined) {
264 FlushMMEInlineDraw();
265 }
259} 266}
260 267
261void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) { 268void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
@@ -416,6 +423,97 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
416 } 423 }
417} 424}
418 425
426void Maxwell3D::StepInstance(const MMEDrawMode expected_mode, const u32 count) {
427 if (mme_draw.current_mode == MMEDrawMode::Undefined) {
428 if (mme_draw.gl_begin_consume) {
429 mme_draw.current_mode = expected_mode;
430 mme_draw.current_count = count;
431 mme_draw.instance_count = 1;
432 mme_draw.gl_begin_consume = false;
433 mme_draw.gl_end_count = 0;
434 }
435 return;
436 } else {
437 if (mme_draw.current_mode == expected_mode && count == mme_draw.current_count &&
438 mme_draw.instance_mode && mme_draw.gl_begin_consume) {
439 mme_draw.instance_count++;
440 mme_draw.gl_begin_consume = false;
441 return;
442 } else {
443 FlushMMEInlineDraw();
444 }
445 }
446 // Tail call in case it needs to retry.
447 StepInstance(expected_mode, count);
448}
449
450void Maxwell3D::CallMethodFromMME(const GPU::MethodCall& method_call) {
451 const u32 method = method_call.method;
452 if (mme_inline[method]) {
453 regs.reg_array[method] = method_call.argument;
454 if (method == MAXWELL3D_REG_INDEX(vertex_buffer.count) ||
455 method == MAXWELL3D_REG_INDEX(index_array.count)) {
456 const MMEDrawMode expected_mode = method == MAXWELL3D_REG_INDEX(vertex_buffer.count)
457 ? MMEDrawMode::Array
458 : MMEDrawMode::Indexed;
459 StepInstance(expected_mode, method_call.argument);
460 } else if (method == MAXWELL3D_REG_INDEX(draw.vertex_begin_gl)) {
461 mme_draw.instance_mode =
462 (regs.draw.instance_next != 0) || (regs.draw.instance_cont != 0);
463 mme_draw.gl_begin_consume = true;
464 } else {
465 mme_draw.gl_end_count++;
466 }
467 } else {
468 if (mme_draw.current_mode != MMEDrawMode::Undefined) {
469 FlushMMEInlineDraw();
470 }
471 CallMethod(method_call);
472 }
473}
474
475void Maxwell3D::FlushMMEInlineDraw() {
476 LOG_DEBUG(HW_GPU, "called, topology={}, count={}", static_cast<u32>(regs.draw.topology.Value()),
477 regs.vertex_buffer.count);
478 ASSERT_MSG(!(regs.index_array.count && regs.vertex_buffer.count), "Both indexed and direct?");
479 ASSERT(mme_draw.instance_count == mme_draw.gl_end_count);
480
481 auto debug_context = system.GetGPUDebugContext();
482
483 if (debug_context) {
484 debug_context->OnEvent(Tegra::DebugContext::Event::IncomingPrimitiveBatch, nullptr);
485 }
486
487 // Both instance configuration registers can not be set at the same time.
488 ASSERT_MSG(!regs.draw.instance_next || !regs.draw.instance_cont,
489 "Illegal combination of instancing parameters");
490
491 const bool is_indexed = mme_draw.current_mode == MMEDrawMode::Indexed;
492 if (ShouldExecute()) {
493 rasterizer.DrawMultiBatch(is_indexed);
494 }
495
496 if (debug_context) {
497 debug_context->OnEvent(Tegra::DebugContext::Event::FinishedPrimitiveBatch, nullptr);
498 }
499
500 // TODO(bunnei): Below, we reset vertex count so that we can use these registers to determine if
501 // the game is trying to draw indexed or direct mode. This needs to be verified on HW still -
502 // it's possible that it is incorrect and that there is some other register used to specify the
503 // drawing mode.
504 if (is_indexed) {
505 regs.index_array.count = 0;
506 } else {
507 regs.vertex_buffer.count = 0;
508 }
509 mme_draw.current_mode = MMEDrawMode::Undefined;
510 mme_draw.current_count = 0;
511 mme_draw.instance_count = 0;
512 mme_draw.instance_mode = false;
513 mme_draw.gl_begin_consume = false;
514 mme_draw.gl_end_count = 0;
515}
516
419void Maxwell3D::ProcessMacroUpload(u32 data) { 517void Maxwell3D::ProcessMacroUpload(u32 data) {
420 ASSERT_MSG(regs.macros.upload_address < macro_memory.size(), 518 ASSERT_MSG(regs.macros.upload_address < macro_memory.size(),
421 "upload_address exceeded macro_memory size!"); 519 "upload_address exceeded macro_memory size!");
@@ -564,7 +662,9 @@ void Maxwell3D::DrawArrays() {
564 } 662 }
565 663
566 const bool is_indexed{regs.index_array.count && !regs.vertex_buffer.count}; 664 const bool is_indexed{regs.index_array.count && !regs.vertex_buffer.count};
567 rasterizer.AccelerateDrawBatch(is_indexed); 665 if (ShouldExecute()) {
666 rasterizer.DrawBatch(is_indexed);
667 }
568 668
569 if (debug_context) { 669 if (debug_context) {
570 debug_context->OnEvent(Tegra::DebugContext::Event::FinishedPrimitiveBatch, nullptr); 670 debug_context->OnEvent(Tegra::DebugContext::Event::FinishedPrimitiveBatch, nullptr);
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h
index e5ec90717..4c97759ed 100644
--- a/src/video_core/engines/maxwell_3d.h
+++ b/src/video_core/engines/maxwell_3d.h
@@ -811,8 +811,9 @@ public:
811 INSERT_PADDING_WORDS(0x21); 811 INSERT_PADDING_WORDS(0x21);
812 812
813 u32 vb_element_base; 813 u32 vb_element_base;
814 u32 vb_base_instance;
814 815
815 INSERT_PADDING_WORDS(0x36); 816 INSERT_PADDING_WORDS(0x35);
816 817
817 union { 818 union {
818 BitField<0, 1, u32> c0; 819 BitField<0, 1, u32> c0;
@@ -1238,6 +1239,11 @@ public:
1238 /// Write the value to the register identified by method. 1239 /// Write the value to the register identified by method.
1239 void CallMethod(const GPU::MethodCall& method_call); 1240 void CallMethod(const GPU::MethodCall& method_call);
1240 1241
1242 /// Write the value to the register identified by method.
1243 void CallMethodFromMME(const GPU::MethodCall& method_call);
1244
1245 void FlushMMEInlineDraw();
1246
1241 /// Given a Texture Handle, returns the TSC and TIC entries. 1247 /// Given a Texture Handle, returns the TSC and TIC entries.
1242 Texture::FullTextureInfo GetTextureInfo(const Texture::TextureHandle tex_handle, 1248 Texture::FullTextureInfo GetTextureInfo(const Texture::TextureHandle tex_handle,
1243 std::size_t offset) const; 1249 std::size_t offset) const;
@@ -1263,6 +1269,21 @@ public:
1263 return execute_on; 1269 return execute_on;
1264 } 1270 }
1265 1271
1272 enum class MMEDrawMode : u32 {
1273 Undefined,
1274 Array,
1275 Indexed,
1276 };
1277
1278 struct MMEDrawState {
1279 MMEDrawMode current_mode{MMEDrawMode::Undefined};
1280 u32 current_count{};
1281 u32 instance_count{};
1282 bool instance_mode{};
1283 bool gl_begin_consume{};
1284 u32 gl_end_count{};
1285 } mme_draw;
1286
1266private: 1287private:
1267 void InitializeRegisterDefaults(); 1288 void InitializeRegisterDefaults();
1268 1289
@@ -1275,6 +1296,8 @@ private:
1275 /// Start offsets of each macro in macro_memory 1296 /// Start offsets of each macro in macro_memory
1276 std::array<u32, 0x80> macro_positions = {}; 1297 std::array<u32, 0x80> macro_positions = {};
1277 1298
1299 std::array<bool, Regs::NUM_REGS> mme_inline{};
1300
1278 /// Memory for macro code 1301 /// Memory for macro code
1279 MacroMemory macro_memory; 1302 MacroMemory macro_memory;
1280 1303
@@ -1346,6 +1369,9 @@ private:
1346 1369
1347 /// Handles a write to the VERTEX_END_GL register, triggering a draw. 1370 /// Handles a write to the VERTEX_END_GL register, triggering a draw.
1348 void DrawArrays(); 1371 void DrawArrays();
1372
1373 // Handles a instance drawcall from MME
1374 void StepInstance(MMEDrawMode expected_mode, u32 count);
1349}; 1375};
1350 1376
1351#define ASSERT_REG_POSITION(field_name, position) \ 1377#define ASSERT_REG_POSITION(field_name, position) \
@@ -1402,6 +1428,7 @@ ASSERT_REG_POSITION(stencil_front_mask, 0x4E7);
1402ASSERT_REG_POSITION(frag_color_clamp, 0x4EA); 1428ASSERT_REG_POSITION(frag_color_clamp, 0x4EA);
1403ASSERT_REG_POSITION(screen_y_control, 0x4EB); 1429ASSERT_REG_POSITION(screen_y_control, 0x4EB);
1404ASSERT_REG_POSITION(vb_element_base, 0x50D); 1430ASSERT_REG_POSITION(vb_element_base, 0x50D);
1431ASSERT_REG_POSITION(vb_base_instance, 0x50E);
1405ASSERT_REG_POSITION(clip_distance_enabled, 0x544); 1432ASSERT_REG_POSITION(clip_distance_enabled, 0x544);
1406ASSERT_REG_POSITION(point_size, 0x546); 1433ASSERT_REG_POSITION(point_size, 0x546);
1407ASSERT_REG_POSITION(zeta_enable, 0x54E); 1434ASSERT_REG_POSITION(zeta_enable, 0x54E);
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index 28272ef6f..7a6355ce2 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -544,7 +544,7 @@ enum class VoteOperation : u64 {
544 Eq = 2, // allThreadsEqualNV 544 Eq = 2, // allThreadsEqualNV
545}; 545};
546 546
547enum class ImageAtomicSize : u64 { 547enum class ImageAtomicOperationType : u64 {
548 U32 = 0, 548 U32 = 0,
549 S32 = 1, 549 S32 = 1,
550 U64 = 2, 550 U64 = 2,
@@ -1432,11 +1432,11 @@ union Instruction {
1432 ASSERT(mode == SurfaceDataMode::D_BA); 1432 ASSERT(mode == SurfaceDataMode::D_BA);
1433 return store_data_layout; 1433 return store_data_layout;
1434 } 1434 }
1435 } sust; 1435 } suldst;
1436 1436
1437 union { 1437 union {
1438 BitField<28, 1, u64> is_ba; 1438 BitField<28, 1, u64> is_ba;
1439 BitField<51, 3, ImageAtomicSize> size; 1439 BitField<51, 3, ImageAtomicOperationType> operation_type;
1440 BitField<33, 3, ImageType> image_type; 1440 BitField<33, 3, ImageType> image_type;
1441 BitField<29, 4, ImageAtomicOperation> operation; 1441 BitField<29, 4, ImageAtomicOperation> operation;
1442 BitField<49, 2, OutOfBoundsStore> out_of_bounds_store; 1442 BitField<49, 2, OutOfBoundsStore> out_of_bounds_store;
@@ -1595,6 +1595,7 @@ public:
1595 TMML_B, // Texture Mip Map Level 1595 TMML_B, // Texture Mip Map Level
1596 TMML, // Texture Mip Map Level 1596 TMML, // Texture Mip Map Level
1597 SUST, // Surface Store 1597 SUST, // Surface Store
1598 SULD, // Surface Load
1598 SUATOM, // Surface Atomic Operation 1599 SUATOM, // Surface Atomic Operation
1599 EXIT, 1600 EXIT,
1600 NOP, 1601 NOP,
@@ -1884,6 +1885,7 @@ private:
1884 INST("110111110110----", Id::TMML_B, Type::Texture, "TMML_B"), 1885 INST("110111110110----", Id::TMML_B, Type::Texture, "TMML_B"),
1885 INST("1101111101011---", Id::TMML, Type::Texture, "TMML"), 1886 INST("1101111101011---", Id::TMML, Type::Texture, "TMML"),
1886 INST("11101011001-----", Id::SUST, Type::Image, "SUST"), 1887 INST("11101011001-----", Id::SUST, Type::Image, "SUST"),
1888 INST("11101011000-----", Id::SULD, Type::Image, "SULD"),
1887 INST("1110101000------", Id::SUATOM, Type::Image, "SUATOM_D"), 1889 INST("1110101000------", Id::SUATOM, Type::Image, "SUATOM_D"),
1888 INST("0101000010110---", Id::NOP, Type::Trivial, "NOP"), 1890 INST("0101000010110---", Id::NOP, Type::Trivial, "NOP"),
1889 INST("11100000--------", Id::IPA, Type::Trivial, "IPA"), 1891 INST("11100000--------", Id::IPA, Type::Trivial, "IPA"),
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index 2c47541cb..76cfe8107 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -122,6 +122,7 @@ u32 RenderTargetBytesPerPixel(RenderTargetFormat format) {
122 case RenderTargetFormat::RGBA16_UINT: 122 case RenderTargetFormat::RGBA16_UINT:
123 case RenderTargetFormat::RGBA16_UNORM: 123 case RenderTargetFormat::RGBA16_UNORM:
124 case RenderTargetFormat::RGBA16_FLOAT: 124 case RenderTargetFormat::RGBA16_FLOAT:
125 case RenderTargetFormat::RGBX16_FLOAT:
125 case RenderTargetFormat::RG32_FLOAT: 126 case RenderTargetFormat::RG32_FLOAT:
126 case RenderTargetFormat::RG32_UINT: 127 case RenderTargetFormat::RG32_UINT:
127 return 8; 128 return 8;
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 78bc0601a..29fa8e95b 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -42,6 +42,7 @@ enum class RenderTargetFormat : u32 {
42 RGBA16_FLOAT = 0xCA, 42 RGBA16_FLOAT = 0xCA,
43 RG32_FLOAT = 0xCB, 43 RG32_FLOAT = 0xCB,
44 RG32_UINT = 0xCD, 44 RG32_UINT = 0xCD,
45 RGBX16_FLOAT = 0xCE,
45 BGRA8_UNORM = 0xCF, 46 BGRA8_UNORM = 0xCF,
46 BGRA8_SRGB = 0xD0, 47 BGRA8_SRGB = 0xD0,
47 RGB10_A2_UNORM = 0xD1, 48 RGB10_A2_UNORM = 0xD1,
diff --git a/src/video_core/macro_interpreter.cpp b/src/video_core/macro_interpreter.cpp
index 62afc0d11..dbaeac6db 100644
--- a/src/video_core/macro_interpreter.cpp
+++ b/src/video_core/macro_interpreter.cpp
@@ -257,7 +257,7 @@ void MacroInterpreter::SetMethodAddress(u32 address) {
257} 257}
258 258
259void MacroInterpreter::Send(u32 value) { 259void MacroInterpreter::Send(u32 value) {
260 maxwell3d.CallMethod({method_address.address, value}); 260 maxwell3d.CallMethodFromMME({method_address.address, value});
261 // Increment the method address by the method increment. 261 // Increment the method address by the method increment.
262 method_address.address.Assign(method_address.address.Value() + 262 method_address.address.Assign(method_address.address.Value() +
263 method_address.increment.Value()); 263 method_address.increment.Value());
diff --git a/src/video_core/morton.cpp b/src/video_core/morton.cpp
index 084f85e67..ab71870ab 100644
--- a/src/video_core/morton.cpp
+++ b/src/video_core/morton.cpp
@@ -83,6 +83,7 @@ static constexpr ConversionArray morton_to_linear_fns = {
83 MortonCopy<true, PixelFormat::RG8U>, 83 MortonCopy<true, PixelFormat::RG8U>,
84 MortonCopy<true, PixelFormat::RG8S>, 84 MortonCopy<true, PixelFormat::RG8S>,
85 MortonCopy<true, PixelFormat::RG32UI>, 85 MortonCopy<true, PixelFormat::RG32UI>,
86 MortonCopy<true, PixelFormat::RGBX16F>,
86 MortonCopy<true, PixelFormat::R32UI>, 87 MortonCopy<true, PixelFormat::R32UI>,
87 MortonCopy<true, PixelFormat::ASTC_2D_8X8>, 88 MortonCopy<true, PixelFormat::ASTC_2D_8X8>,
88 MortonCopy<true, PixelFormat::ASTC_2D_8X5>, 89 MortonCopy<true, PixelFormat::ASTC_2D_8X5>,
@@ -151,6 +152,7 @@ static constexpr ConversionArray linear_to_morton_fns = {
151 MortonCopy<false, PixelFormat::RG8U>, 152 MortonCopy<false, PixelFormat::RG8U>,
152 MortonCopy<false, PixelFormat::RG8S>, 153 MortonCopy<false, PixelFormat::RG8S>,
153 MortonCopy<false, PixelFormat::RG32UI>, 154 MortonCopy<false, PixelFormat::RG32UI>,
155 MortonCopy<false, PixelFormat::RGBX16F>,
154 MortonCopy<false, PixelFormat::R32UI>, 156 MortonCopy<false, PixelFormat::R32UI>,
155 nullptr, 157 nullptr,
156 nullptr, 158 nullptr,
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h
index 6b3f2d50a..5b0eca9e2 100644
--- a/src/video_core/rasterizer_interface.h
+++ b/src/video_core/rasterizer_interface.h
@@ -29,7 +29,10 @@ public:
29 virtual ~RasterizerInterface() {} 29 virtual ~RasterizerInterface() {}
30 30
31 /// Draw the current batch of vertex arrays 31 /// Draw the current batch of vertex arrays
32 virtual void DrawArrays() = 0; 32 virtual bool DrawBatch(bool is_indexed) = 0;
33
34 /// Draw the current batch of multiple instances of vertex arrays
35 virtual bool DrawMultiBatch(bool is_indexed) = 0;
33 36
34 /// Clear the current framebuffer 37 /// Clear the current framebuffer
35 virtual void Clear() = 0; 38 virtual void Clear() = 0;
@@ -69,10 +72,6 @@ public:
69 return false; 72 return false;
70 } 73 }
71 74
72 virtual bool AccelerateDrawBatch(bool is_indexed) {
73 return false;
74 }
75
76 /// Increase/decrease the number of object in pages touching the specified region 75 /// Increase/decrease the number of object in pages touching the specified region
77 virtual void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {} 76 virtual void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {}
78 77
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp
index 4f59a87b4..64de7e425 100644
--- a/src/video_core/renderer_opengl/gl_device.cpp
+++ b/src/video_core/renderer_opengl/gl_device.cpp
@@ -2,8 +2,10 @@
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>
5#include <array> 6#include <array>
6#include <cstddef> 7#include <cstddef>
8#include <vector>
7#include <glad/glad.h> 9#include <glad/glad.h>
8 10
9#include "common/logging/log.h" 11#include "common/logging/log.h"
@@ -30,9 +32,27 @@ bool TestProgram(const GLchar* glsl) {
30 return link_status == GL_TRUE; 32 return link_status == GL_TRUE;
31} 33}
32 34
35std::vector<std::string_view> GetExtensions() {
36 GLint num_extensions;
37 glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions);
38 std::vector<std::string_view> extensions;
39 extensions.reserve(num_extensions);
40 for (GLint index = 0; index < num_extensions; ++index) {
41 extensions.push_back(
42 reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, static_cast<GLuint>(index))));
43 }
44 return extensions;
45}
46
47bool HasExtension(const std::vector<std::string_view>& images, std::string_view extension) {
48 return std::find(images.begin(), images.end(), extension) != images.end();
49}
50
33} // Anonymous namespace 51} // Anonymous namespace
34 52
35Device::Device() { 53Device::Device() {
54 const std::vector extensions = GetExtensions();
55
36 uniform_buffer_alignment = GetInteger<std::size_t>(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT); 56 uniform_buffer_alignment = GetInteger<std::size_t>(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT);
37 shader_storage_alignment = GetInteger<std::size_t>(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT); 57 shader_storage_alignment = GetInteger<std::size_t>(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT);
38 max_vertex_attributes = GetInteger<u32>(GL_MAX_VERTEX_ATTRIBS); 58 max_vertex_attributes = GetInteger<u32>(GL_MAX_VERTEX_ATTRIBS);
@@ -40,6 +60,7 @@ Device::Device() {
40 has_warp_intrinsics = GLAD_GL_NV_gpu_shader5 && GLAD_GL_NV_shader_thread_group && 60 has_warp_intrinsics = GLAD_GL_NV_gpu_shader5 && GLAD_GL_NV_shader_thread_group &&
41 GLAD_GL_NV_shader_thread_shuffle; 61 GLAD_GL_NV_shader_thread_shuffle;
42 has_vertex_viewport_layer = GLAD_GL_ARB_shader_viewport_layer_array; 62 has_vertex_viewport_layer = GLAD_GL_ARB_shader_viewport_layer_array;
63 has_image_load_formatted = HasExtension(extensions, "GL_EXT_shader_image_load_formatted");
43 has_variable_aoffi = TestVariableAoffi(); 64 has_variable_aoffi = TestVariableAoffi();
44 has_component_indexing_bug = TestComponentIndexingBug(); 65 has_component_indexing_bug = TestComponentIndexingBug();
45 has_precise_bug = TestPreciseBug(); 66 has_precise_bug = TestPreciseBug();
@@ -55,6 +76,7 @@ Device::Device(std::nullptr_t) {
55 max_varyings = 15; 76 max_varyings = 15;
56 has_warp_intrinsics = true; 77 has_warp_intrinsics = true;
57 has_vertex_viewport_layer = true; 78 has_vertex_viewport_layer = true;
79 has_image_load_formatted = true;
58 has_variable_aoffi = true; 80 has_variable_aoffi = true;
59 has_component_indexing_bug = false; 81 has_component_indexing_bug = false;
60 has_precise_bug = false; 82 has_precise_bug = false;
diff --git a/src/video_core/renderer_opengl/gl_device.h b/src/video_core/renderer_opengl/gl_device.h
index ba6dcd3be..bb273c3d6 100644
--- a/src/video_core/renderer_opengl/gl_device.h
+++ b/src/video_core/renderer_opengl/gl_device.h
@@ -38,6 +38,10 @@ public:
38 return has_vertex_viewport_layer; 38 return has_vertex_viewport_layer;
39 } 39 }
40 40
41 bool HasImageLoadFormatted() const {
42 return has_image_load_formatted;
43 }
44
41 bool HasVariableAoffi() const { 45 bool HasVariableAoffi() const {
42 return has_variable_aoffi; 46 return has_variable_aoffi;
43 } 47 }
@@ -61,6 +65,7 @@ private:
61 u32 max_varyings{}; 65 u32 max_varyings{};
62 bool has_warp_intrinsics{}; 66 bool has_warp_intrinsics{};
63 bool has_vertex_viewport_layer{}; 67 bool has_vertex_viewport_layer{};
68 bool has_image_load_formatted{};
64 bool has_variable_aoffi{}; 69 bool has_variable_aoffi{};
65 bool has_component_indexing_bug{}; 70 bool has_component_indexing_bug{};
66 bool has_precise_bug{}; 71 bool has_precise_bug{};
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index a2c1473db..6a17bed72 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -49,40 +49,6 @@ MICROPROFILE_DEFINE(OpenGL_Blits, "OpenGL", "Blits", MP_RGB(128, 128, 192));
49MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100)); 49MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100));
50MICROPROFILE_DEFINE(OpenGL_PrimitiveAssembly, "OpenGL", "Prim Asmbl", MP_RGB(255, 100, 100)); 50MICROPROFILE_DEFINE(OpenGL_PrimitiveAssembly, "OpenGL", "Prim Asmbl", MP_RGB(255, 100, 100));
51 51
52struct DrawParameters {
53 GLenum primitive_mode;
54 GLsizei count;
55 GLint current_instance;
56 bool use_indexed;
57
58 GLint vertex_first;
59
60 GLenum index_format;
61 GLint base_vertex;
62 GLintptr index_buffer_offset;
63
64 void DispatchDraw() const {
65 if (use_indexed) {
66 const auto index_buffer_ptr = reinterpret_cast<const void*>(index_buffer_offset);
67 if (current_instance > 0) {
68 glDrawElementsInstancedBaseVertexBaseInstance(primitive_mode, count, index_format,
69 index_buffer_ptr, 1, base_vertex,
70 current_instance);
71 } else {
72 glDrawElementsBaseVertex(primitive_mode, count, index_format, index_buffer_ptr,
73 base_vertex);
74 }
75 } else {
76 if (current_instance > 0) {
77 glDrawArraysInstancedBaseInstance(primitive_mode, vertex_first, count, 1,
78 current_instance);
79 } else {
80 glDrawArrays(primitive_mode, vertex_first, count);
81 }
82 }
83 }
84};
85
86static std::size_t GetConstBufferSize(const Tegra::Engines::ConstBufferInfo& buffer, 52static std::size_t GetConstBufferSize(const Tegra::Engines::ConstBufferInfo& buffer,
87 const GLShader::ConstBufferEntry& entry) { 53 const GLShader::ConstBufferEntry& entry) {
88 if (!entry.IsIndirect()) { 54 if (!entry.IsIndirect()) {
@@ -270,29 +236,6 @@ GLintptr RasterizerOpenGL::SetupIndexBuffer() {
270 return offset; 236 return offset;
271} 237}
272 238
273DrawParameters RasterizerOpenGL::SetupDraw(GLintptr index_buffer_offset) {
274 const auto& gpu = system.GPU().Maxwell3D();
275 const auto& regs = gpu.regs;
276 const bool is_indexed = accelerate_draw == AccelDraw::Indexed;
277
278 DrawParameters params{};
279 params.current_instance = gpu.state.current_instance;
280
281 params.use_indexed = is_indexed;
282 params.primitive_mode = MaxwellToGL::PrimitiveTopology(regs.draw.topology);
283
284 if (is_indexed) {
285 params.index_format = MaxwellToGL::IndexFormat(regs.index_array.format);
286 params.count = regs.index_array.count;
287 params.index_buffer_offset = index_buffer_offset;
288 params.base_vertex = static_cast<GLint>(regs.vb_element_base);
289 } else {
290 params.count = regs.vertex_buffer.count;
291 params.vertex_first = regs.vertex_buffer.first;
292 }
293 return params;
294}
295
296void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) { 239void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
297 MICROPROFILE_SCOPE(OpenGL_Shader); 240 MICROPROFILE_SCOPE(OpenGL_Shader);
298 auto& gpu = system.GPU().Maxwell3D(); 241 auto& gpu = system.GPU().Maxwell3D();
@@ -399,12 +342,6 @@ std::size_t RasterizerOpenGL::CalculateIndexBufferSize() const {
399 static_cast<std::size_t>(regs.index_array.FormatSizeInBytes()); 342 static_cast<std::size_t>(regs.index_array.FormatSizeInBytes());
400} 343}
401 344
402bool RasterizerOpenGL::AccelerateDrawBatch(bool is_indexed) {
403 accelerate_draw = is_indexed ? AccelDraw::Indexed : AccelDraw::Arrays;
404 DrawArrays();
405 return true;
406}
407
408template <typename Map, typename Interval> 345template <typename Map, typename Interval>
409static constexpr auto RangeFromInterval(Map& map, const Interval& interval) { 346static constexpr auto RangeFromInterval(Map& map, const Interval& interval) {
410 return boost::make_iterator_range(map.equal_range(interval)); 347 return boost::make_iterator_range(map.equal_range(interval));
@@ -640,17 +577,9 @@ void RasterizerOpenGL::Clear() {
640 } 577 }
641} 578}
642 579
643void RasterizerOpenGL::DrawArrays() { 580void RasterizerOpenGL::DrawPrelude() {
644 if (accelerate_draw == AccelDraw::Disabled)
645 return;
646
647 MICROPROFILE_SCOPE(OpenGL_Drawing);
648 auto& gpu = system.GPU().Maxwell3D(); 581 auto& gpu = system.GPU().Maxwell3D();
649 582
650 if (!gpu.ShouldExecute()) {
651 return;
652 }
653
654 SyncColorMask(); 583 SyncColorMask();
655 SyncFragmentColorClampState(); 584 SyncFragmentColorClampState();
656 SyncMultiSampleState(); 585 SyncMultiSampleState();
@@ -695,10 +624,7 @@ void RasterizerOpenGL::DrawArrays() {
695 // Upload vertex and index data. 624 // Upload vertex and index data.
696 SetupVertexBuffer(vao); 625 SetupVertexBuffer(vao);
697 SetupVertexInstances(vao); 626 SetupVertexInstances(vao);
698 const GLintptr index_buffer_offset = SetupIndexBuffer(); 627 index_buffer_offset = SetupIndexBuffer();
699
700 // Setup draw parameters. It will automatically choose what glDraw* method to use.
701 const DrawParameters params = SetupDraw(index_buffer_offset);
702 628
703 // Prepare packed bindings. 629 // Prepare packed bindings.
704 bind_ubo_pushbuffer.Setup(0); 630 bind_ubo_pushbuffer.Setup(0);
@@ -706,7 +632,8 @@ void RasterizerOpenGL::DrawArrays() {
706 632
707 // Setup shaders and their used resources. 633 // Setup shaders and their used resources.
708 texture_cache.GuardSamplers(true); 634 texture_cache.GuardSamplers(true);
709 SetupShaders(params.primitive_mode); 635 const auto primitive_mode = MaxwellToGL::PrimitiveTopology(gpu.regs.draw.topology);
636 SetupShaders(primitive_mode);
710 texture_cache.GuardSamplers(false); 637 texture_cache.GuardSamplers(false);
711 638
712 ConfigureFramebuffers(); 639 ConfigureFramebuffers();
@@ -730,11 +657,107 @@ void RasterizerOpenGL::DrawArrays() {
730 if (texture_cache.TextureBarrier()) { 657 if (texture_cache.TextureBarrier()) {
731 glTextureBarrier(); 658 glTextureBarrier();
732 } 659 }
660}
661
662struct DrawParams {
663 bool is_indexed{};
664 bool is_instanced{};
665 GLenum primitive_mode{};
666 GLint count{};
667 GLint base_vertex{};
668
669 // Indexed settings
670 GLenum index_format{};
671 GLintptr index_buffer_offset{};
672
673 // Instanced setting
674 GLint num_instances{};
675 GLint base_instance{};
676
677 void DispatchDraw() {
678 if (is_indexed) {
679 const auto index_buffer_ptr = reinterpret_cast<const void*>(index_buffer_offset);
680 if (is_instanced) {
681 glDrawElementsInstancedBaseVertexBaseInstance(primitive_mode, count, index_format,
682 index_buffer_ptr, num_instances,
683 base_vertex, base_instance);
684 } else {
685 glDrawElementsBaseVertex(primitive_mode, count, index_format, index_buffer_ptr,
686 base_vertex);
687 }
688 } else {
689 if (is_instanced) {
690 glDrawArraysInstancedBaseInstance(primitive_mode, base_vertex, count, num_instances,
691 base_instance);
692 } else {
693 glDrawArrays(primitive_mode, base_vertex, count);
694 }
695 }
696 }
697};
733 698
734 params.DispatchDraw(); 699bool RasterizerOpenGL::DrawBatch(bool is_indexed) {
700 accelerate_draw = is_indexed ? AccelDraw::Indexed : AccelDraw::Arrays;
701
702 MICROPROFILE_SCOPE(OpenGL_Drawing);
735 703
704 DrawPrelude();
705
706 auto& maxwell3d = system.GPU().Maxwell3D();
707 const auto& regs = maxwell3d.regs;
708 const auto current_instance = maxwell3d.state.current_instance;
709 DrawParams draw_call{};
710 draw_call.is_indexed = is_indexed;
711 draw_call.num_instances = static_cast<GLint>(1);
712 draw_call.base_instance = static_cast<GLint>(current_instance);
713 draw_call.is_instanced = current_instance > 0;
714 draw_call.primitive_mode = MaxwellToGL::PrimitiveTopology(regs.draw.topology);
715 if (draw_call.is_indexed) {
716 draw_call.count = static_cast<GLint>(regs.index_array.count);
717 draw_call.base_vertex = static_cast<GLint>(regs.vb_element_base);
718 draw_call.index_format = MaxwellToGL::IndexFormat(regs.index_array.format);
719 draw_call.index_buffer_offset = index_buffer_offset;
720 } else {
721 draw_call.count = static_cast<GLint>(regs.vertex_buffer.count);
722 draw_call.base_vertex = static_cast<GLint>(regs.vertex_buffer.first);
723 }
724 draw_call.DispatchDraw();
725
726 maxwell3d.dirty.memory_general = false;
736 accelerate_draw = AccelDraw::Disabled; 727 accelerate_draw = AccelDraw::Disabled;
737 gpu.dirty.memory_general = false; 728 return true;
729}
730
731bool RasterizerOpenGL::DrawMultiBatch(bool is_indexed) {
732 accelerate_draw = is_indexed ? AccelDraw::Indexed : AccelDraw::Arrays;
733
734 MICROPROFILE_SCOPE(OpenGL_Drawing);
735
736 DrawPrelude();
737
738 auto& maxwell3d = system.GPU().Maxwell3D();
739 const auto& regs = maxwell3d.regs;
740 const auto& draw_setup = maxwell3d.mme_draw;
741 DrawParams draw_call{};
742 draw_call.is_indexed = is_indexed;
743 draw_call.num_instances = static_cast<GLint>(draw_setup.instance_count);
744 draw_call.base_instance = static_cast<GLint>(regs.vb_base_instance);
745 draw_call.is_instanced = draw_setup.instance_count > 1;
746 draw_call.primitive_mode = MaxwellToGL::PrimitiveTopology(regs.draw.topology);
747 if (draw_call.is_indexed) {
748 draw_call.count = static_cast<GLint>(regs.index_array.count);
749 draw_call.base_vertex = static_cast<GLint>(regs.vb_element_base);
750 draw_call.index_format = MaxwellToGL::IndexFormat(regs.index_array.format);
751 draw_call.index_buffer_offset = index_buffer_offset;
752 } else {
753 draw_call.count = static_cast<GLint>(regs.vertex_buffer.count);
754 draw_call.base_vertex = static_cast<GLint>(regs.vertex_buffer.first);
755 }
756 draw_call.DispatchDraw();
757
758 maxwell3d.dirty.memory_general = false;
759 accelerate_draw = AccelDraw::Disabled;
760 return true;
738} 761}
739 762
740void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) { 763void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) {
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index 4f5c7f864..9c10ebda3 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -57,7 +57,8 @@ public:
57 ScreenInfo& info); 57 ScreenInfo& info);
58 ~RasterizerOpenGL() override; 58 ~RasterizerOpenGL() override;
59 59
60 void DrawArrays() override; 60 bool DrawBatch(bool is_indexed) override;
61 bool DrawMultiBatch(bool is_indexed) override;
61 void Clear() override; 62 void Clear() override;
62 void DispatchCompute(GPUVAddr code_addr) override; 63 void DispatchCompute(GPUVAddr code_addr) override;
63 void FlushAll() override; 64 void FlushAll() override;
@@ -71,7 +72,6 @@ public:
71 const Tegra::Engines::Fermi2D::Config& copy_config) override; 72 const Tegra::Engines::Fermi2D::Config& copy_config) override;
72 bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr, 73 bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr,
73 u32 pixel_stride) override; 74 u32 pixel_stride) override;
74 bool AccelerateDrawBatch(bool is_indexed) override;
75 void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) override; 75 void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) override;
76 void LoadDiskResources(const std::atomic_bool& stop_loading, 76 void LoadDiskResources(const std::atomic_bool& stop_loading,
77 const VideoCore::DiskResourceLoadCallback& callback) override; 77 const VideoCore::DiskResourceLoadCallback& callback) override;
@@ -105,6 +105,9 @@ private:
105 void SetupGlobalMemory(const GLShader::GlobalMemoryEntry& entry, GPUVAddr gpu_addr, 105 void SetupGlobalMemory(const GLShader::GlobalMemoryEntry& entry, GPUVAddr gpu_addr,
106 std::size_t size); 106 std::size_t size);
107 107
108 /// Syncs all the state, shaders, render targets and textures setting before a draw call.
109 void DrawPrelude();
110
108 /// Configures the current textures to use for the draw command. Returns shaders texture buffer 111 /// Configures the current textures to use for the draw command. Returns shaders texture buffer
109 /// usage. 112 /// usage.
110 TextureBufferUsage SetupDrawTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, 113 TextureBufferUsage SetupDrawTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage,
@@ -216,7 +219,7 @@ private:
216 219
217 GLintptr SetupIndexBuffer(); 220 GLintptr SetupIndexBuffer();
218 221
219 DrawParameters SetupDraw(GLintptr index_buffer_offset); 222 GLintptr index_buffer_offset;
220 223
221 void SetupShaders(GLenum primitive_mode); 224 void SetupShaders(GLenum primitive_mode);
222 225
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index 0dbc4c02f..42ca3b1bd 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -211,14 +211,14 @@ CachedProgram SpecializeShader(const std::string& code, const GLShader::ShaderEn
211 const auto primitive_mode{variant.primitive_mode}; 211 const auto primitive_mode{variant.primitive_mode};
212 const auto texture_buffer_usage{variant.texture_buffer_usage}; 212 const auto texture_buffer_usage{variant.texture_buffer_usage};
213 213
214 std::string source = "#version 430 core\n" 214 std::string source = R"(#version 430 core
215 "#extension GL_ARB_separate_shader_objects : enable\n" 215#extension GL_ARB_separate_shader_objects : enable
216 "#extension GL_NV_gpu_shader5 : enable\n" 216#extension GL_ARB_shader_viewport_layer_array : enable
217 "#extension GL_NV_shader_thread_group : enable\n" 217#extension GL_EXT_shader_image_load_formatted : enable
218 "#extension GL_NV_shader_thread_shuffle : enable\n"; 218#extension GL_NV_gpu_shader5 : enable
219 if (entries.shader_viewport_layer_array) { 219#extension GL_NV_shader_thread_group : enable
220 source += "#extension GL_ARB_shader_viewport_layer_array : enable\n"; 220#extension GL_NV_shader_thread_shuffle : enable
221 } 221)";
222 if (program_type == ProgramType::Compute) { 222 if (program_type == ProgramType::Compute) {
223 source += "#extension GL_ARB_compute_variable_group_size : require\n"; 223 source += "#extension GL_ARB_compute_variable_group_size : require\n";
224 } 224 }
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 76439e7ab..8fa9e6534 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -19,6 +19,7 @@
19#include "video_core/renderer_opengl/gl_device.h" 19#include "video_core/renderer_opengl/gl_device.h"
20#include "video_core/renderer_opengl/gl_rasterizer.h" 20#include "video_core/renderer_opengl/gl_rasterizer.h"
21#include "video_core/renderer_opengl/gl_shader_decompiler.h" 21#include "video_core/renderer_opengl/gl_shader_decompiler.h"
22#include "video_core/shader/node.h"
22#include "video_core/shader/shader_ir.h" 23#include "video_core/shader/shader_ir.h"
23 24
24namespace OpenGL::GLShader { 25namespace OpenGL::GLShader {
@@ -241,6 +242,26 @@ constexpr const char* GetTypeString(Type type) {
241 } 242 }
242} 243}
243 244
245constexpr const char* GetImageTypeDeclaration(Tegra::Shader::ImageType image_type) {
246 switch (image_type) {
247 case Tegra::Shader::ImageType::Texture1D:
248 return "1D";
249 case Tegra::Shader::ImageType::TextureBuffer:
250 return "Buffer";
251 case Tegra::Shader::ImageType::Texture1DArray:
252 return "1DArray";
253 case Tegra::Shader::ImageType::Texture2D:
254 return "2D";
255 case Tegra::Shader::ImageType::Texture2DArray:
256 return "2DArray";
257 case Tegra::Shader::ImageType::Texture3D:
258 return "3D";
259 default:
260 UNREACHABLE();
261 return "1D";
262 }
263}
264
244/// Generates code to use for a swizzle operation. 265/// Generates code to use for a swizzle operation.
245constexpr const char* GetSwizzle(u32 element) { 266constexpr const char* GetSwizzle(u32 element) {
246 constexpr std::array swizzle = {".x", ".y", ".z", ".w"}; 267 constexpr std::array swizzle = {".x", ".y", ".z", ".w"};
@@ -398,8 +419,6 @@ public:
398 usage.is_read, usage.is_written); 419 usage.is_read, usage.is_written);
399 } 420 }
400 entries.clip_distances = ir.GetClipDistances(); 421 entries.clip_distances = ir.GetClipDistances();
401 entries.shader_viewport_layer_array =
402 IsVertexShader(stage) && (ir.UsesLayer() || ir.UsesViewportIndex());
403 entries.shader_length = ir.GetLength(); 422 entries.shader_length = ir.GetLength();
404 return entries; 423 return entries;
405 } 424 }
@@ -462,6 +481,14 @@ private:
462 code.AddLine("float gl_PointSize;"); 481 code.AddLine("float gl_PointSize;");
463 } 482 }
464 483
484 if (ir.UsesInstanceId()) {
485 code.AddLine("int gl_InstanceID;");
486 }
487
488 if (ir.UsesVertexId()) {
489 code.AddLine("int gl_VertexID;");
490 }
491
465 --code.scope; 492 --code.scope;
466 code.AddLine("}};"); 493 code.AddLine("}};");
467 code.AddNewLine(); 494 code.AddNewLine();
@@ -714,42 +741,6 @@ private:
714 void DeclareImages() { 741 void DeclareImages() {
715 const auto& images{ir.GetImages()}; 742 const auto& images{ir.GetImages()};
716 for (const auto& [offset, image] : images) { 743 for (const auto& [offset, image] : images) {
717 const char* image_type = [&] {
718 switch (image.GetType()) {
719 case Tegra::Shader::ImageType::Texture1D:
720 return "image1D";
721 case Tegra::Shader::ImageType::TextureBuffer:
722 return "imageBuffer";
723 case Tegra::Shader::ImageType::Texture1DArray:
724 return "image1DArray";
725 case Tegra::Shader::ImageType::Texture2D:
726 return "image2D";
727 case Tegra::Shader::ImageType::Texture2DArray:
728 return "image2DArray";
729 case Tegra::Shader::ImageType::Texture3D:
730 return "image3D";
731 default:
732 UNREACHABLE();
733 return "image1D";
734 }
735 }();
736
737 const auto [type_prefix, format] = [&]() -> std::pair<const char*, const char*> {
738 if (!image.IsSizeKnown()) {
739 return {"", ""};
740 }
741 switch (image.GetSize()) {
742 case Tegra::Shader::ImageAtomicSize::U32:
743 return {"u", "r32ui, "};
744 case Tegra::Shader::ImageAtomicSize::S32:
745 return {"i", "r32i, "};
746 default:
747 UNIMPLEMENTED_MSG("Unimplemented atomic size={}",
748 static_cast<u32>(image.GetSize()));
749 return {"", ""};
750 }
751 }();
752
753 std::string qualifier = "coherent volatile"; 744 std::string qualifier = "coherent volatile";
754 if (image.IsRead() && !image.IsWritten()) { 745 if (image.IsRead() && !image.IsWritten()) {
755 qualifier += " readonly"; 746 qualifier += " readonly";
@@ -757,9 +748,10 @@ private:
757 qualifier += " writeonly"; 748 qualifier += " writeonly";
758 } 749 }
759 750
760 code.AddLine("layout (binding = IMAGE_BINDING_{}) {} uniform " 751 const char* format = image.IsAtomic() ? "r32ui, " : "";
761 "{} {};", 752 const char* type_declaration = GetImageTypeDeclaration(image.GetType());
762 image.GetIndex(), qualifier, image_type, GetImage(image)); 753 code.AddLine("layout ({}binding = IMAGE_BINDING_{}) {} uniform uimage{} {};", format,
754 image.GetIndex(), qualifier, type_declaration, GetImage(image));
763 } 755 }
764 if (!images.empty()) { 756 if (!images.empty()) {
765 code.AddNewLine(); 757 code.AddNewLine();
@@ -964,7 +956,7 @@ private:
964 switch (element) { 956 switch (element) {
965 case 2: 957 case 2:
966 // Config pack's first value is instance_id. 958 // Config pack's first value is instance_id.
967 return {"config_pack[0]", Type::Uint}; 959 return {"gl_InstanceID", Type::Int};
968 case 3: 960 case 3:
969 return {"gl_VertexID", Type::Int}; 961 return {"gl_VertexID", Type::Int};
970 } 962 }
@@ -1226,28 +1218,13 @@ private:
1226 } 1218 }
1227 1219
1228 std::string BuildImageValues(Operation operation) { 1220 std::string BuildImageValues(Operation operation) {
1221 constexpr std::array constructors{"uint", "uvec2", "uvec3", "uvec4"};
1229 const auto meta{std::get<MetaImage>(operation.GetMeta())}; 1222 const auto meta{std::get<MetaImage>(operation.GetMeta())};
1230 const auto [constructors, type] = [&]() -> std::pair<std::array<const char*, 4>, Type> {
1231 constexpr std::array float_constructors{"float", "vec2", "vec3", "vec4"};
1232 if (!meta.image.IsSizeKnown()) {
1233 return {float_constructors, Type::Float};
1234 }
1235 switch (meta.image.GetSize()) {
1236 case Tegra::Shader::ImageAtomicSize::U32:
1237 return {{"uint", "uvec2", "uvec3", "uvec4"}, Type::Uint};
1238 case Tegra::Shader::ImageAtomicSize::S32:
1239 return {{"int", "ivec2", "ivec3", "ivec4"}, Type::Uint};
1240 default:
1241 UNIMPLEMENTED_MSG("Unimplemented image size={}",
1242 static_cast<u32>(meta.image.GetSize()));
1243 return {float_constructors, Type::Float};
1244 }
1245 }();
1246 1223
1247 const std::size_t values_count{meta.values.size()}; 1224 const std::size_t values_count{meta.values.size()};
1248 std::string expr = fmt::format("{}(", constructors.at(values_count - 1)); 1225 std::string expr = fmt::format("{}(", constructors.at(values_count - 1));
1249 for (std::size_t i = 0; i < values_count; ++i) { 1226 for (std::size_t i = 0; i < values_count; ++i) {
1250 expr += Visit(meta.values.at(i)).As(type); 1227 expr += Visit(meta.values.at(i)).AsUint();
1251 if (i + 1 < values_count) { 1228 if (i + 1 < values_count) {
1252 expr += ", "; 1229 expr += ", ";
1253 } 1230 }
@@ -1256,29 +1233,6 @@ private:
1256 return expr; 1233 return expr;
1257 } 1234 }
1258 1235
1259 Expression AtomicImage(Operation operation, const char* opname) {
1260 constexpr std::array constructors{"int(", "ivec2(", "ivec3(", "ivec4("};
1261 const auto meta{std::get<MetaImage>(operation.GetMeta())};
1262 ASSERT(meta.values.size() == 1);
1263 ASSERT(meta.image.IsSizeKnown());
1264
1265 const auto type = [&]() {
1266 switch (const auto size = meta.image.GetSize()) {
1267 case Tegra::Shader::ImageAtomicSize::U32:
1268 return Type::Uint;
1269 case Tegra::Shader::ImageAtomicSize::S32:
1270 return Type::Int;
1271 default:
1272 UNIMPLEMENTED_MSG("Unimplemented image size={}", static_cast<u32>(size));
1273 return Type::Uint;
1274 }
1275 }();
1276
1277 return {fmt::format("{}({}, {}, {})", opname, GetImage(meta.image),
1278 BuildIntegerCoordinates(operation), Visit(meta.values[0]).As(type)),
1279 type};
1280 }
1281
1282 Expression Assign(Operation operation) { 1236 Expression Assign(Operation operation) {
1283 const Node& dest = operation[0]; 1237 const Node& dest = operation[0];
1284 const Node& src = operation[1]; 1238 const Node& src = operation[1];
@@ -1537,6 +1491,8 @@ private:
1537 case Tegra::Shader::HalfType::H1_H1: 1491 case Tegra::Shader::HalfType::H1_H1:
1538 return {fmt::format("vec2({}[1])", operand.AsHalfFloat()), Type::HalfFloat}; 1492 return {fmt::format("vec2({}[1])", operand.AsHalfFloat()), Type::HalfFloat};
1539 } 1493 }
1494 UNREACHABLE();
1495 return {"0", Type::Int};
1540 } 1496 }
1541 1497
1542 Expression HMergeF32(Operation operation) { 1498 Expression HMergeF32(Operation operation) {
@@ -1801,6 +1757,19 @@ private:
1801 return {tmp, Type::Float}; 1757 return {tmp, Type::Float};
1802 } 1758 }
1803 1759
1760 Expression ImageLoad(Operation operation) {
1761 if (!device.HasImageLoadFormatted()) {
1762 LOG_ERROR(Render_OpenGL,
1763 "Device lacks GL_EXT_shader_image_load_formatted, stubbing image load");
1764 return {"0", Type::Int};
1765 }
1766
1767 const auto meta{std::get<MetaImage>(operation.GetMeta())};
1768 return {fmt::format("imageLoad({}, {}){}", GetImage(meta.image),
1769 BuildIntegerCoordinates(operation), GetSwizzle(meta.element)),
1770 Type::Uint};
1771 }
1772
1804 Expression ImageStore(Operation operation) { 1773 Expression ImageStore(Operation operation) {
1805 const auto meta{std::get<MetaImage>(operation.GetMeta())}; 1774 const auto meta{std::get<MetaImage>(operation.GetMeta())};
1806 code.AddLine("imageStore({}, {}, {});", GetImage(meta.image), 1775 code.AddLine("imageStore({}, {}, {});", GetImage(meta.image),
@@ -1808,31 +1777,14 @@ private:
1808 return {}; 1777 return {};
1809 } 1778 }
1810 1779
1811 Expression AtomicImageAdd(Operation operation) { 1780 template <const std::string_view& opname>
1812 return AtomicImage(operation, "imageAtomicAdd"); 1781 Expression AtomicImage(Operation operation) {
1813 } 1782 const auto meta{std::get<MetaImage>(operation.GetMeta())};
1814 1783 ASSERT(meta.values.size() == 1);
1815 Expression AtomicImageMin(Operation operation) {
1816 return AtomicImage(operation, "imageAtomicMin");
1817 }
1818
1819 Expression AtomicImageMax(Operation operation) {
1820 return AtomicImage(operation, "imageAtomicMax");
1821 }
1822 Expression AtomicImageAnd(Operation operation) {
1823 return AtomicImage(operation, "imageAtomicAnd");
1824 }
1825
1826 Expression AtomicImageOr(Operation operation) {
1827 return AtomicImage(operation, "imageAtomicOr");
1828 }
1829
1830 Expression AtomicImageXor(Operation operation) {
1831 return AtomicImage(operation, "imageAtomicXor");
1832 }
1833 1784
1834 Expression AtomicImageExchange(Operation operation) { 1785 return {fmt::format("imageAtomic{}({}, {}, {})", opname, GetImage(meta.image),
1835 return AtomicImage(operation, "imageAtomicExchange"); 1786 BuildIntegerCoordinates(operation), Visit(meta.values[0]).AsUint()),
1787 Type::Uint};
1836 } 1788 }
1837 1789
1838 Expression Branch(Operation operation) { 1790 Expression Branch(Operation operation) {
@@ -2027,6 +1979,12 @@ private:
2027 Func() = delete; 1979 Func() = delete;
2028 ~Func() = delete; 1980 ~Func() = delete;
2029 1981
1982 static constexpr std::string_view Add = "Add";
1983 static constexpr std::string_view And = "And";
1984 static constexpr std::string_view Or = "Or";
1985 static constexpr std::string_view Xor = "Xor";
1986 static constexpr std::string_view Exchange = "Exchange";
1987
2030 static constexpr std::string_view ShuffleIndexed = "shuffleNV"; 1988 static constexpr std::string_view ShuffleIndexed = "shuffleNV";
2031 static constexpr std::string_view ShuffleUp = "shuffleUpNV"; 1989 static constexpr std::string_view ShuffleUp = "shuffleUpNV";
2032 static constexpr std::string_view ShuffleDown = "shuffleDownNV"; 1990 static constexpr std::string_view ShuffleDown = "shuffleDownNV";
@@ -2164,14 +2122,14 @@ private:
2164 &GLSLDecompiler::TextureQueryLod, 2122 &GLSLDecompiler::TextureQueryLod,
2165 &GLSLDecompiler::TexelFetch, 2123 &GLSLDecompiler::TexelFetch,
2166 2124
2125 &GLSLDecompiler::ImageLoad,
2167 &GLSLDecompiler::ImageStore, 2126 &GLSLDecompiler::ImageStore,
2168 &GLSLDecompiler::AtomicImageAdd, 2127
2169 &GLSLDecompiler::AtomicImageMin, 2128 &GLSLDecompiler::AtomicImage<Func::Add>,
2170 &GLSLDecompiler::AtomicImageMax, 2129 &GLSLDecompiler::AtomicImage<Func::And>,
2171 &GLSLDecompiler::AtomicImageAnd, 2130 &GLSLDecompiler::AtomicImage<Func::Or>,
2172 &GLSLDecompiler::AtomicImageOr, 2131 &GLSLDecompiler::AtomicImage<Func::Xor>,
2173 &GLSLDecompiler::AtomicImageXor, 2132 &GLSLDecompiler::AtomicImage<Func::Exchange>,
2174 &GLSLDecompiler::AtomicImageExchange,
2175 2133
2176 &GLSLDecompiler::Branch, 2134 &GLSLDecompiler::Branch,
2177 &GLSLDecompiler::BranchIndirect, 2135 &GLSLDecompiler::BranchIndirect,
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.h b/src/video_core/renderer_opengl/gl_shader_decompiler.h
index 2ea02f5bf..e538dc001 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.h
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.h
@@ -90,7 +90,6 @@ struct ShaderEntries {
90 std::vector<ImageEntry> images; 90 std::vector<ImageEntry> images;
91 std::vector<GlobalMemoryEntry> global_memory_entries; 91 std::vector<GlobalMemoryEntry> global_memory_entries;
92 std::array<bool, Maxwell::NumClipDistances> clip_distances{}; 92 std::array<bool, Maxwell::NumClipDistances> clip_distances{};
93 bool shader_viewport_layer_array{};
94 std::size_t shader_length{}; 93 std::size_t shader_length{};
95}; 94};
96 95
diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
index f141c4e3b..6a7012b54 100644
--- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
@@ -343,20 +343,17 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEn
343 u8 is_bindless{}; 343 u8 is_bindless{};
344 u8 is_written{}; 344 u8 is_written{};
345 u8 is_read{}; 345 u8 is_read{};
346 u8 is_size_known{}; 346 u8 is_atomic{};
347 u32 size{};
348 if (!LoadObjectFromPrecompiled(offset) || !LoadObjectFromPrecompiled(index) || 347 if (!LoadObjectFromPrecompiled(offset) || !LoadObjectFromPrecompiled(index) ||
349 !LoadObjectFromPrecompiled(type) || !LoadObjectFromPrecompiled(is_bindless) || 348 !LoadObjectFromPrecompiled(type) || !LoadObjectFromPrecompiled(is_bindless) ||
350 !LoadObjectFromPrecompiled(is_written) || !LoadObjectFromPrecompiled(is_read) || 349 !LoadObjectFromPrecompiled(is_written) || !LoadObjectFromPrecompiled(is_read) ||
351 !LoadObjectFromPrecompiled(is_size_known) || !LoadObjectFromPrecompiled(size)) { 350 !LoadObjectFromPrecompiled(is_atomic)) {
352 return {}; 351 return {};
353 } 352 }
354 entry.entries.images.emplace_back( 353 entry.entries.images.emplace_back(
355 static_cast<std::size_t>(offset), static_cast<std::size_t>(index), 354 static_cast<std::size_t>(offset), static_cast<std::size_t>(index),
356 static_cast<Tegra::Shader::ImageType>(type), is_bindless != 0, is_written != 0, 355 static_cast<Tegra::Shader::ImageType>(type), is_bindless != 0, is_written != 0,
357 is_read != 0, 356 is_read != 0, is_atomic != 0);
358 is_size_known ? std::make_optional(static_cast<Tegra::Shader::ImageAtomicSize>(size))
359 : std::nullopt);
360 } 357 }
361 358
362 u32 global_memory_count{}; 359 u32 global_memory_count{};
@@ -382,12 +379,6 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCacheOpenGL::LoadDecompiledEn
382 } 379 }
383 } 380 }
384 381
385 bool shader_viewport_layer_array{};
386 if (!LoadObjectFromPrecompiled(shader_viewport_layer_array)) {
387 return {};
388 }
389 entry.entries.shader_viewport_layer_array = shader_viewport_layer_array;
390
391 u64 shader_length{}; 382 u64 shader_length{};
392 if (!LoadObjectFromPrecompiled(shader_length)) { 383 if (!LoadObjectFromPrecompiled(shader_length)) {
393 return {}; 384 return {};
@@ -435,14 +426,13 @@ bool ShaderDiskCacheOpenGL::SaveDecompiledFile(u64 unique_identifier, const std:
435 return false; 426 return false;
436 } 427 }
437 for (const auto& image : entries.images) { 428 for (const auto& image : entries.images) {
438 const u32 size = image.IsSizeKnown() ? static_cast<u32>(image.GetSize()) : 0U;
439 if (!SaveObjectToPrecompiled(static_cast<u64>(image.GetOffset())) || 429 if (!SaveObjectToPrecompiled(static_cast<u64>(image.GetOffset())) ||
440 !SaveObjectToPrecompiled(static_cast<u64>(image.GetIndex())) || 430 !SaveObjectToPrecompiled(static_cast<u64>(image.GetIndex())) ||
441 !SaveObjectToPrecompiled(static_cast<u32>(image.GetType())) || 431 !SaveObjectToPrecompiled(static_cast<u32>(image.GetType())) ||
442 !SaveObjectToPrecompiled(static_cast<u8>(image.IsBindless() ? 1 : 0)) || 432 !SaveObjectToPrecompiled(static_cast<u8>(image.IsBindless() ? 1 : 0)) ||
443 !SaveObjectToPrecompiled(static_cast<u8>(image.IsWritten() ? 1 : 0)) || 433 !SaveObjectToPrecompiled(static_cast<u8>(image.IsWritten() ? 1 : 0)) ||
444 !SaveObjectToPrecompiled(static_cast<u8>(image.IsRead() ? 1 : 0)) || 434 !SaveObjectToPrecompiled(static_cast<u8>(image.IsRead() ? 1 : 0)) ||
445 !SaveObjectToPrecompiled(image.IsSizeKnown()) || !SaveObjectToPrecompiled(size)) { 435 !SaveObjectToPrecompiled(static_cast<u8>(image.IsAtomic() ? 1 : 0))) {
446 return false; 436 return false;
447 } 437 }
448 } 438 }
@@ -464,10 +454,6 @@ bool ShaderDiskCacheOpenGL::SaveDecompiledFile(u64 unique_identifier, const std:
464 } 454 }
465 } 455 }
466 456
467 if (!SaveObjectToPrecompiled(entries.shader_viewport_layer_array)) {
468 return false;
469 }
470
471 if (!SaveObjectToPrecompiled(static_cast<u64>(entries.shader_length))) { 457 if (!SaveObjectToPrecompiled(static_cast<u64>(entries.shader_length))) {
472 return false; 458 return false;
473 } 459 }
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index 4f135fe03..173b76c4e 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -97,6 +97,7 @@ constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex_format
97 {GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // RG8U 97 {GL_RG8, GL_RG, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // RG8U
98 {GL_RG8, GL_RG, GL_BYTE, ComponentType::SNorm, false}, // RG8S 98 {GL_RG8, GL_RG, GL_BYTE, ComponentType::SNorm, false}, // RG8S
99 {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RG32UI 99 {GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // RG32UI
100 {GL_RGB16F, GL_RGBA16, GL_HALF_FLOAT, ComponentType::Float, false}, // RGBX16F
100 {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // R32UI 101 {GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, ComponentType::UInt, false}, // R32UI
101 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8 102 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X8
102 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5 103 {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_8X5
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
index 0bbbf6851..3c5acda3e 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
@@ -143,6 +143,7 @@ static constexpr std::array<FormatTuple, VideoCore::Surface::MaxPixelFormat> tex
143 {vk::Format::eUndefined, ComponentType::Invalid, false}, // RG8U 143 {vk::Format::eUndefined, ComponentType::Invalid, false}, // RG8U
144 {vk::Format::eUndefined, ComponentType::Invalid, false}, // RG8S 144 {vk::Format::eUndefined, ComponentType::Invalid, false}, // RG8S
145 {vk::Format::eUndefined, ComponentType::Invalid, false}, // RG32UI 145 {vk::Format::eUndefined, ComponentType::Invalid, false}, // RG32UI
146 {vk::Format::eUndefined, ComponentType::Invalid, false}, // RGBX16F
146 {vk::Format::eUndefined, ComponentType::Invalid, false}, // R32UI 147 {vk::Format::eUndefined, ComponentType::Invalid, false}, // R32UI
147 {vk::Format::eUndefined, ComponentType::Invalid, false}, // ASTC_2D_8X8 148 {vk::Format::eUndefined, ComponentType::Invalid, false}, // ASTC_2D_8X8
148 {vk::Format::eUndefined, ComponentType::Invalid, false}, // ASTC_2D_8X5 149 {vk::Format::eUndefined, ComponentType::Invalid, false}, // ASTC_2D_8X5
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
index f7fbbb6e4..77fc58f25 100644
--- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
+++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
@@ -19,6 +19,7 @@
19#include "video_core/engines/shader_header.h" 19#include "video_core/engines/shader_header.h"
20#include "video_core/renderer_vulkan/vk_device.h" 20#include "video_core/renderer_vulkan/vk_device.h"
21#include "video_core/renderer_vulkan/vk_shader_decompiler.h" 21#include "video_core/renderer_vulkan/vk_shader_decompiler.h"
22#include "video_core/shader/node.h"
22#include "video_core/shader/shader_ir.h" 23#include "video_core/shader/shader_ir.h"
23 24
24namespace Vulkan::VKShader { 25namespace Vulkan::VKShader {
@@ -939,22 +940,17 @@ private:
939 return {}; 940 return {};
940 } 941 }
941 942
942 Id ImageStore(Operation operation) { 943 Id ImageLoad(Operation operation) {
943 UNIMPLEMENTED();
944 return {};
945 }
946
947 Id AtomicImageAdd(Operation operation) {
948 UNIMPLEMENTED(); 944 UNIMPLEMENTED();
949 return {}; 945 return {};
950 } 946 }
951 947
952 Id AtomicImageMin(Operation operation) { 948 Id ImageStore(Operation operation) {
953 UNIMPLEMENTED(); 949 UNIMPLEMENTED();
954 return {}; 950 return {};
955 } 951 }
956 952
957 Id AtomicImageMax(Operation operation) { 953 Id AtomicImageAdd(Operation operation) {
958 UNIMPLEMENTED(); 954 UNIMPLEMENTED();
959 return {}; 955 return {};
960 } 956 }
@@ -1440,10 +1436,9 @@ private:
1440 &SPIRVDecompiler::TextureQueryLod, 1436 &SPIRVDecompiler::TextureQueryLod,
1441 &SPIRVDecompiler::TexelFetch, 1437 &SPIRVDecompiler::TexelFetch,
1442 1438
1439 &SPIRVDecompiler::ImageLoad,
1443 &SPIRVDecompiler::ImageStore, 1440 &SPIRVDecompiler::ImageStore,
1444 &SPIRVDecompiler::AtomicImageAdd, 1441 &SPIRVDecompiler::AtomicImageAdd,
1445 &SPIRVDecompiler::AtomicImageMin,
1446 &SPIRVDecompiler::AtomicImageMax,
1447 &SPIRVDecompiler::AtomicImageAnd, 1442 &SPIRVDecompiler::AtomicImageAnd,
1448 &SPIRVDecompiler::AtomicImageOr, 1443 &SPIRVDecompiler::AtomicImageOr,
1449 &SPIRVDecompiler::AtomicImageXor, 1444 &SPIRVDecompiler::AtomicImageXor,
diff --git a/src/video_core/shader/decode/image.cpp b/src/video_core/shader/decode/image.cpp
index d54fb88c9..95ec1cdd9 100644
--- a/src/video_core/shader/decode/image.cpp
+++ b/src/video_core/shader/decode/image.cpp
@@ -41,11 +41,46 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) {
41 const Instruction instr = {program_code[pc]}; 41 const Instruction instr = {program_code[pc]};
42 const auto opcode = OpCode::Decode(instr); 42 const auto opcode = OpCode::Decode(instr);
43 43
44 const auto GetCoordinates = [this, instr](Tegra::Shader::ImageType image_type) {
45 std::vector<Node> coords;
46 const std::size_t num_coords{GetImageTypeNumCoordinates(image_type)};
47 coords.reserve(num_coords);
48 for (std::size_t i = 0; i < num_coords; ++i) {
49 coords.push_back(GetRegister(instr.gpr8.Value() + i));
50 }
51 return coords;
52 };
53
44 switch (opcode->get().GetId()) { 54 switch (opcode->get().GetId()) {
55 case OpCode::Id::SULD: {
56 UNIMPLEMENTED_IF(instr.suldst.mode != Tegra::Shader::SurfaceDataMode::P);
57 UNIMPLEMENTED_IF(instr.suldst.out_of_bounds_store !=
58 Tegra::Shader::OutOfBoundsStore::Ignore);
59
60 const auto type{instr.suldst.image_type};
61 auto& image{instr.suldst.is_immediate ? GetImage(instr.image, type)
62 : GetBindlessImage(instr.gpr39, type)};
63 image.MarkRead();
64
65 u32 indexer = 0;
66 for (u32 element = 0; element < 4; ++element) {
67 if (!instr.suldst.IsComponentEnabled(element)) {
68 continue;
69 }
70 MetaImage meta{image, {}, element};
71 Node value = Operation(OperationCode::ImageLoad, meta, GetCoordinates(type));
72 SetTemporary(bb, indexer++, std::move(value));
73 }
74 for (u32 i = 0; i < indexer; ++i) {
75 SetRegister(bb, instr.gpr0.Value() + i, GetTemporary(i));
76 }
77 break;
78 }
45 case OpCode::Id::SUST: { 79 case OpCode::Id::SUST: {
46 UNIMPLEMENTED_IF(instr.sust.mode != Tegra::Shader::SurfaceDataMode::P); 80 UNIMPLEMENTED_IF(instr.suldst.mode != Tegra::Shader::SurfaceDataMode::P);
47 UNIMPLEMENTED_IF(instr.sust.out_of_bounds_store != Tegra::Shader::OutOfBoundsStore::Ignore); 81 UNIMPLEMENTED_IF(instr.suldst.out_of_bounds_store !=
48 UNIMPLEMENTED_IF(instr.sust.component_mask_selector != 0xf); // Ensure we have an RGBA store 82 Tegra::Shader::OutOfBoundsStore::Ignore);
83 UNIMPLEMENTED_IF(instr.suldst.component_mask_selector != 0xf); // Ensure we have RGBA
49 84
50 std::vector<Node> values; 85 std::vector<Node> values;
51 constexpr std::size_t hardcoded_size{4}; 86 constexpr std::size_t hardcoded_size{4};
@@ -53,58 +88,51 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) {
53 values.push_back(GetRegister(instr.gpr0.Value() + i)); 88 values.push_back(GetRegister(instr.gpr0.Value() + i));
54 } 89 }
55 90
56 std::vector<Node> coords; 91 const auto type{instr.suldst.image_type};
57 const std::size_t num_coords{GetImageTypeNumCoordinates(instr.sust.image_type)}; 92 auto& image{instr.suldst.is_immediate ? GetImage(instr.image, type)
58 for (std::size_t i = 0; i < num_coords; ++i) { 93 : GetBindlessImage(instr.gpr39, type)};
59 coords.push_back(GetRegister(instr.gpr8.Value() + i));
60 }
61
62 const auto type{instr.sust.image_type};
63 auto& image{instr.sust.is_immediate ? GetImage(instr.image, type)
64 : GetBindlessImage(instr.gpr39, type)};
65 image.MarkWrite(); 94 image.MarkWrite();
66 95
67 MetaImage meta{image, values}; 96 MetaImage meta{image, std::move(values)};
68 bb.push_back(Operation(OperationCode::ImageStore, meta, std::move(coords))); 97 bb.push_back(Operation(OperationCode::ImageStore, meta, GetCoordinates(type)));
69 break; 98 break;
70 } 99 }
71 case OpCode::Id::SUATOM: { 100 case OpCode::Id::SUATOM: {
72 UNIMPLEMENTED_IF(instr.suatom_d.is_ba != 0); 101 UNIMPLEMENTED_IF(instr.suatom_d.is_ba != 0);
73 102
74 Node value = GetRegister(instr.gpr0);
75
76 std::vector<Node> coords;
77 const std::size_t num_coords{GetImageTypeNumCoordinates(instr.sust.image_type)};
78 for (std::size_t i = 0; i < num_coords; ++i) {
79 coords.push_back(GetRegister(instr.gpr8.Value() + i));
80 }
81
82 const OperationCode operation_code = [instr] { 103 const OperationCode operation_code = [instr] {
83 switch (instr.suatom_d.operation) { 104 switch (instr.suatom_d.operation_type) {
84 case Tegra::Shader::ImageAtomicOperation::Add: 105 case Tegra::Shader::ImageAtomicOperationType::S32:
85 return OperationCode::AtomicImageAdd; 106 case Tegra::Shader::ImageAtomicOperationType::U32:
86 case Tegra::Shader::ImageAtomicOperation::Min: 107 switch (instr.suatom_d.operation) {
87 return OperationCode::AtomicImageMin; 108 case Tegra::Shader::ImageAtomicOperation::Add:
88 case Tegra::Shader::ImageAtomicOperation::Max: 109 return OperationCode::AtomicImageAdd;
89 return OperationCode::AtomicImageMax; 110 case Tegra::Shader::ImageAtomicOperation::And:
90 case Tegra::Shader::ImageAtomicOperation::And: 111 return OperationCode::AtomicImageAnd;
91 return OperationCode::AtomicImageAnd; 112 case Tegra::Shader::ImageAtomicOperation::Or:
92 case Tegra::Shader::ImageAtomicOperation::Or: 113 return OperationCode::AtomicImageOr;
93 return OperationCode::AtomicImageOr; 114 case Tegra::Shader::ImageAtomicOperation::Xor:
94 case Tegra::Shader::ImageAtomicOperation::Xor: 115 return OperationCode::AtomicImageXor;
95 return OperationCode::AtomicImageXor; 116 case Tegra::Shader::ImageAtomicOperation::Exch:
96 case Tegra::Shader::ImageAtomicOperation::Exch: 117 return OperationCode::AtomicImageExchange;
97 return OperationCode::AtomicImageExchange; 118 }
98 default: 119 default:
99 UNIMPLEMENTED_MSG("Unimplemented operation={}", 120 break;
100 static_cast<u32>(instr.suatom_d.operation.Value()));
101 return OperationCode::AtomicImageAdd;
102 } 121 }
122 UNIMPLEMENTED_MSG("Unimplemented operation={} type={}",
123 static_cast<u64>(instr.suatom_d.operation.Value()),
124 static_cast<u64>(instr.suatom_d.operation_type.Value()));
125 return OperationCode::AtomicImageAdd;
103 }(); 126 }();
104 127
105 const auto& image{GetImage(instr.image, instr.suatom_d.image_type, instr.suatom_d.size)}; 128 Node value = GetRegister(instr.gpr0);
129
130 const auto type = instr.suatom_d.image_type;
131 auto& image = GetImage(instr.image, type);
132 image.MarkAtomic();
133
106 MetaImage meta{image, {std::move(value)}}; 134 MetaImage meta{image, {std::move(value)}};
107 SetRegister(bb, instr.gpr0, Operation(operation_code, meta, std::move(coords))); 135 SetRegister(bb, instr.gpr0, Operation(operation_code, meta, GetCoordinates(type)));
108 break; 136 break;
109 } 137 }
110 default: 138 default:
@@ -114,35 +142,32 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) {
114 return pc; 142 return pc;
115} 143}
116 144
117Image& ShaderIR::GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type, 145Image& ShaderIR::GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type) {
118 std::optional<Tegra::Shader::ImageAtomicSize> size) {
119 const auto offset{static_cast<std::size_t>(image.index.Value())}; 146 const auto offset{static_cast<std::size_t>(image.index.Value())};
120 if (const auto image = TryUseExistingImage(offset, type, size)) { 147 if (const auto image = TryUseExistingImage(offset, type)) {
121 return *image; 148 return *image;
122 } 149 }
123 150
124 const std::size_t next_index{used_images.size()}; 151 const std::size_t next_index{used_images.size()};
125 return used_images.emplace(offset, Image{offset, next_index, type, size}).first->second; 152 return used_images.emplace(offset, Image{offset, next_index, type}).first->second;
126} 153}
127 154
128Image& ShaderIR::GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type, 155Image& ShaderIR::GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type) {
129 std::optional<Tegra::Shader::ImageAtomicSize> size) {
130 const Node image_register{GetRegister(reg)}; 156 const Node image_register{GetRegister(reg)};
131 const auto [base_image, cbuf_index, cbuf_offset]{ 157 const auto [base_image, cbuf_index, cbuf_offset]{
132 TrackCbuf(image_register, global_code, static_cast<s64>(global_code.size()))}; 158 TrackCbuf(image_register, global_code, static_cast<s64>(global_code.size()))};
133 const auto cbuf_key{(static_cast<u64>(cbuf_index) << 32) | static_cast<u64>(cbuf_offset)}; 159 const auto cbuf_key{(static_cast<u64>(cbuf_index) << 32) | static_cast<u64>(cbuf_offset)};
134 160
135 if (const auto image = TryUseExistingImage(cbuf_key, type, size)) { 161 if (const auto image = TryUseExistingImage(cbuf_key, type)) {
136 return *image; 162 return *image;
137 } 163 }
138 164
139 const std::size_t next_index{used_images.size()}; 165 const std::size_t next_index{used_images.size()};
140 return used_images.emplace(cbuf_key, Image{cbuf_index, cbuf_offset, next_index, type, size}) 166 return used_images.emplace(cbuf_key, Image{cbuf_index, cbuf_offset, next_index, type})
141 .first->second; 167 .first->second;
142} 168}
143 169
144Image* ShaderIR::TryUseExistingImage(u64 offset, Tegra::Shader::ImageType type, 170Image* ShaderIR::TryUseExistingImage(u64 offset, Tegra::Shader::ImageType type) {
145 std::optional<Tegra::Shader::ImageAtomicSize> size) {
146 auto it = used_images.find(offset); 171 auto it = used_images.find(offset);
147 if (it == used_images.end()) { 172 if (it == used_images.end()) {
148 return nullptr; 173 return nullptr;
@@ -150,14 +175,6 @@ Image* ShaderIR::TryUseExistingImage(u64 offset, Tegra::Shader::ImageType type,
150 auto& image = it->second; 175 auto& image = it->second;
151 ASSERT(image.GetType() == type); 176 ASSERT(image.GetType() == type);
152 177
153 if (size) {
154 // We know the size, if it's known it has to be the same as before, otherwise we can set it.
155 if (image.IsSizeKnown()) {
156 ASSERT(image.GetSize() == size);
157 } else {
158 image.SetSize(*size);
159 }
160 }
161 return &image; 178 return &image;
162} 179}
163 180
diff --git a/src/video_core/shader/node.h b/src/video_core/shader/node.h
index abf2cb1ab..338bab17c 100644
--- a/src/video_core/shader/node.h
+++ b/src/video_core/shader/node.h
@@ -149,10 +149,10 @@ enum class OperationCode {
149 TextureQueryLod, /// (MetaTexture, float[N] coords) -> float4 149 TextureQueryLod, /// (MetaTexture, float[N] coords) -> float4
150 TexelFetch, /// (MetaTexture, int[N], int) -> float4 150 TexelFetch, /// (MetaTexture, int[N], int) -> float4
151 151
152 ImageStore, /// (MetaImage, int[N] values) -> void 152 ImageLoad, /// (MetaImage, int[N] coords) -> void
153 ImageStore, /// (MetaImage, int[N] coords) -> void
154
153 AtomicImageAdd, /// (MetaImage, int[N] coords) -> void 155 AtomicImageAdd, /// (MetaImage, int[N] coords) -> void
154 AtomicImageMin, /// (MetaImage, int[N] coords) -> void
155 AtomicImageMax, /// (MetaImage, int[N] coords) -> void
156 AtomicImageAnd, /// (MetaImage, int[N] coords) -> void 156 AtomicImageAnd, /// (MetaImage, int[N] coords) -> void
157 AtomicImageOr, /// (MetaImage, int[N] coords) -> void 157 AtomicImageOr, /// (MetaImage, int[N] coords) -> void
158 AtomicImageXor, /// (MetaImage, int[N] coords) -> void 158 AtomicImageXor, /// (MetaImage, int[N] coords) -> void
@@ -294,21 +294,18 @@ private:
294 294
295class Image final { 295class Image final {
296public: 296public:
297 constexpr explicit Image(std::size_t offset, std::size_t index, Tegra::Shader::ImageType type, 297 constexpr explicit Image(std::size_t offset, std::size_t index, Tegra::Shader::ImageType type)
298 std::optional<Tegra::Shader::ImageAtomicSize> size) 298 : offset{offset}, index{index}, type{type}, is_bindless{false} {}
299 : offset{offset}, index{index}, type{type}, is_bindless{false}, size{size} {}
300 299
301 constexpr explicit Image(u32 cbuf_index, u32 cbuf_offset, std::size_t index, 300 constexpr explicit Image(u32 cbuf_index, u32 cbuf_offset, std::size_t index,
302 Tegra::Shader::ImageType type, 301 Tegra::Shader::ImageType type)
303 std::optional<Tegra::Shader::ImageAtomicSize> size)
304 : offset{(static_cast<u64>(cbuf_index) << 32) | cbuf_offset}, index{index}, type{type}, 302 : offset{(static_cast<u64>(cbuf_index) << 32) | cbuf_offset}, index{index}, type{type},
305 is_bindless{true}, size{size} {} 303 is_bindless{true} {}
306 304
307 constexpr explicit Image(std::size_t offset, std::size_t index, Tegra::Shader::ImageType type, 305 constexpr explicit Image(std::size_t offset, std::size_t index, Tegra::Shader::ImageType type,
308 bool is_bindless, bool is_written, bool is_read, 306 bool is_bindless, bool is_written, bool is_read, bool is_atomic)
309 std::optional<Tegra::Shader::ImageAtomicSize> size)
310 : offset{offset}, index{index}, type{type}, is_bindless{is_bindless}, 307 : offset{offset}, index{index}, type{type}, is_bindless{is_bindless},
311 is_written{is_written}, is_read{is_read}, size{size} {} 308 is_written{is_written}, is_read{is_read}, is_atomic{is_atomic} {}
312 309
313 void MarkWrite() { 310 void MarkWrite() {
314 is_written = true; 311 is_written = true;
@@ -318,8 +315,10 @@ public:
318 is_read = true; 315 is_read = true;
319 } 316 }
320 317
321 void SetSize(Tegra::Shader::ImageAtomicSize size_) { 318 void MarkAtomic() {
322 size = size_; 319 MarkWrite();
320 MarkRead();
321 is_atomic = true;
323 } 322 }
324 323
325 constexpr std::size_t GetOffset() const { 324 constexpr std::size_t GetOffset() const {
@@ -346,21 +345,17 @@ public:
346 return is_read; 345 return is_read;
347 } 346 }
348 347
349 constexpr std::pair<u32, u32> GetBindlessCBuf() const { 348 constexpr bool IsAtomic() const {
350 return {static_cast<u32>(offset >> 32), static_cast<u32>(offset)}; 349 return is_atomic;
351 } 350 }
352 351
353 constexpr bool IsSizeKnown() const { 352 constexpr std::pair<u32, u32> GetBindlessCBuf() const {
354 return size.has_value(); 353 return {static_cast<u32>(offset >> 32), static_cast<u32>(offset)};
355 }
356
357 constexpr Tegra::Shader::ImageAtomicSize GetSize() const {
358 return size.value();
359 } 354 }
360 355
361 constexpr bool operator<(const Image& rhs) const { 356 constexpr bool operator<(const Image& rhs) const {
362 return std::tie(offset, index, type, size, is_bindless) < 357 return std::tie(offset, index, type, is_bindless) <
363 std::tie(rhs.offset, rhs.index, rhs.type, rhs.size, rhs.is_bindless); 358 std::tie(rhs.offset, rhs.index, rhs.type, rhs.is_bindless);
364 } 359 }
365 360
366private: 361private:
@@ -370,7 +365,7 @@ private:
370 bool is_bindless{}; 365 bool is_bindless{};
371 bool is_written{}; 366 bool is_written{};
372 bool is_read{}; 367 bool is_read{};
373 std::optional<Tegra::Shader::ImageAtomicSize> size{}; 368 bool is_atomic{};
374}; 369};
375 370
376struct GlobalMemoryBase { 371struct GlobalMemoryBase {
@@ -402,6 +397,7 @@ struct MetaTexture {
402struct MetaImage { 397struct MetaImage {
403 const Image& image; 398 const Image& image;
404 std::vector<Node> values; 399 std::vector<Node> values;
400 u32 element{};
405}; 401};
406 402
407/// Parameters that modify an operation but are not part of any particular operand 403/// Parameters that modify an operation but are not part of any particular operand
diff --git a/src/video_core/shader/shader_ir.cpp b/src/video_core/shader/shader_ir.cpp
index bbbab0bca..2c357f310 100644
--- a/src/video_core/shader/shader_ir.cpp
+++ b/src/video_core/shader/shader_ir.cpp
@@ -114,6 +114,18 @@ Node ShaderIR::GetOutputAttribute(Attribute::Index index, u64 element, Node buff
114 break; 114 break;
115 } 115 }
116 } 116 }
117 if (index == Attribute::Index::TessCoordInstanceIDVertexID) {
118 switch (element) {
119 case 2:
120 uses_instance_id = true;
121 break;
122 case 3:
123 uses_vertex_id = true;
124 break;
125 default:
126 break;
127 }
128 }
117 if (index == Attribute::Index::ClipDistances0123 || 129 if (index == Attribute::Index::ClipDistances0123 ||
118 index == Attribute::Index::ClipDistances4567) { 130 index == Attribute::Index::ClipDistances4567) {
119 const auto clip_index = 131 const auto clip_index =
diff --git a/src/video_core/shader/shader_ir.h b/src/video_core/shader/shader_ir.h
index 6aed9bb84..6f666ee30 100644
--- a/src/video_core/shader/shader_ir.h
+++ b/src/video_core/shader/shader_ir.h
@@ -124,6 +124,14 @@ public:
124 return uses_point_size; 124 return uses_point_size;
125 } 125 }
126 126
127 bool UsesInstanceId() const {
128 return uses_instance_id;
129 }
130
131 bool UsesVertexId() const {
132 return uses_vertex_id;
133 }
134
127 bool HasPhysicalAttributes() const { 135 bool HasPhysicalAttributes() const {
128 return uses_physical_attributes; 136 return uses_physical_attributes;
129 } 137 }
@@ -276,16 +284,13 @@ private:
276 bool is_shadow); 284 bool is_shadow);
277 285
278 /// Accesses an image. 286 /// Accesses an image.
279 Image& GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type, 287 Image& GetImage(Tegra::Shader::Image image, Tegra::Shader::ImageType type);
280 std::optional<Tegra::Shader::ImageAtomicSize> size = {});
281 288
282 /// Access a bindless image sampler. 289 /// Access a bindless image sampler.
283 Image& GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type, 290 Image& GetBindlessImage(Tegra::Shader::Register reg, Tegra::Shader::ImageType type);
284 std::optional<Tegra::Shader::ImageAtomicSize> size = {});
285 291
286 /// Tries to access an existing image, updating it's state as needed 292 /// Tries to access an existing image, updating it's state as needed
287 Image* TryUseExistingImage(u64 offset, Tegra::Shader::ImageType type, 293 Image* TryUseExistingImage(u64 offset, Tegra::Shader::ImageType type);
288 std::optional<Tegra::Shader::ImageAtomicSize> size);
289 294
290 /// Extracts a sequence of bits from a node 295 /// Extracts a sequence of bits from a node
291 Node BitfieldExtract(Node value, u32 offset, u32 bits); 296 Node BitfieldExtract(Node value, u32 offset, u32 bits);
@@ -373,6 +378,8 @@ private:
373 bool uses_viewport_index{}; 378 bool uses_viewport_index{};
374 bool uses_point_size{}; 379 bool uses_point_size{};
375 bool uses_physical_attributes{}; // Shader uses AL2P or physical attribute read/writes 380 bool uses_physical_attributes{}; // Shader uses AL2P or physical attribute read/writes
381 bool uses_instance_id{};
382 bool uses_vertex_id{};
376 383
377 Tegra::Shader::Header header; 384 Tegra::Shader::Header header;
378}; 385};
diff --git a/src/video_core/surface.cpp b/src/video_core/surface.cpp
index 53d0142cb..250afc6d6 100644
--- a/src/video_core/surface.cpp
+++ b/src/video_core/surface.cpp
@@ -159,6 +159,8 @@ PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format)
159 return PixelFormat::R32UI; 159 return PixelFormat::R32UI;
160 case Tegra::RenderTargetFormat::RG32_UINT: 160 case Tegra::RenderTargetFormat::RG32_UINT:
161 return PixelFormat::RG32UI; 161 return PixelFormat::RG32UI;
162 case Tegra::RenderTargetFormat::RGBX16_FLOAT:
163 return PixelFormat::RGBX16F;
162 default: 164 default:
163 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format)); 165 LOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
164 UNREACHABLE(); 166 UNREACHABLE();
@@ -415,6 +417,7 @@ ComponentType ComponentTypeFromRenderTarget(Tegra::RenderTargetFormat format) {
415 case Tegra::RenderTargetFormat::RG8_SNORM: 417 case Tegra::RenderTargetFormat::RG8_SNORM:
416 return ComponentType::SNorm; 418 return ComponentType::SNorm;
417 case Tegra::RenderTargetFormat::RGBA16_FLOAT: 419 case Tegra::RenderTargetFormat::RGBA16_FLOAT:
420 case Tegra::RenderTargetFormat::RGBX16_FLOAT:
418 case Tegra::RenderTargetFormat::R11G11B10_FLOAT: 421 case Tegra::RenderTargetFormat::R11G11B10_FLOAT:
419 case Tegra::RenderTargetFormat::RGBA32_FLOAT: 422 case Tegra::RenderTargetFormat::RGBA32_FLOAT:
420 case Tegra::RenderTargetFormat::RG32_FLOAT: 423 case Tegra::RenderTargetFormat::RG32_FLOAT:
diff --git a/src/video_core/surface.h b/src/video_core/surface.h
index 19268b7cd..1e1c432a5 100644
--- a/src/video_core/surface.h
+++ b/src/video_core/surface.h
@@ -57,36 +57,37 @@ enum class PixelFormat {
57 RG8U = 39, 57 RG8U = 39,
58 RG8S = 40, 58 RG8S = 40,
59 RG32UI = 41, 59 RG32UI = 41,
60 R32UI = 42, 60 RGBX16F = 42,
61 ASTC_2D_8X8 = 43, 61 R32UI = 43,
62 ASTC_2D_8X5 = 44, 62 ASTC_2D_8X8 = 44,
63 ASTC_2D_5X4 = 45, 63 ASTC_2D_8X5 = 45,
64 BGRA8_SRGB = 46, 64 ASTC_2D_5X4 = 46,
65 DXT1_SRGB = 47, 65 BGRA8_SRGB = 47,
66 DXT23_SRGB = 48, 66 DXT1_SRGB = 48,
67 DXT45_SRGB = 49, 67 DXT23_SRGB = 49,
68 BC7U_SRGB = 50, 68 DXT45_SRGB = 50,
69 ASTC_2D_4X4_SRGB = 51, 69 BC7U_SRGB = 51,
70 ASTC_2D_8X8_SRGB = 52, 70 ASTC_2D_4X4_SRGB = 52,
71 ASTC_2D_8X5_SRGB = 53, 71 ASTC_2D_8X8_SRGB = 53,
72 ASTC_2D_5X4_SRGB = 54, 72 ASTC_2D_8X5_SRGB = 54,
73 ASTC_2D_5X5 = 55, 73 ASTC_2D_5X4_SRGB = 55,
74 ASTC_2D_5X5_SRGB = 56, 74 ASTC_2D_5X5 = 56,
75 ASTC_2D_10X8 = 57, 75 ASTC_2D_5X5_SRGB = 57,
76 ASTC_2D_10X8_SRGB = 58, 76 ASTC_2D_10X8 = 58,
77 ASTC_2D_10X8_SRGB = 59,
77 78
78 MaxColorFormat, 79 MaxColorFormat,
79 80
80 // Depth formats 81 // Depth formats
81 Z32F = 59, 82 Z32F = 60,
82 Z16 = 60, 83 Z16 = 61,
83 84
84 MaxDepthFormat, 85 MaxDepthFormat,
85 86
86 // DepthStencil formats 87 // DepthStencil formats
87 Z24S8 = 61, 88 Z24S8 = 62,
88 S8Z24 = 62, 89 S8Z24 = 63,
89 Z32FS8 = 63, 90 Z32FS8 = 64,
90 91
91 MaxDepthStencilFormat, 92 MaxDepthStencilFormat,
92 93
@@ -166,6 +167,7 @@ constexpr std::array<u32, MaxPixelFormat> compression_factor_shift_table = {{
166 0, // RG8U 167 0, // RG8U
167 0, // RG8S 168 0, // RG8S
168 0, // RG32UI 169 0, // RG32UI
170 0, // RGBX16F
169 0, // R32UI 171 0, // R32UI
170 2, // ASTC_2D_8X8 172 2, // ASTC_2D_8X8
171 2, // ASTC_2D_8X5 173 2, // ASTC_2D_8X5
@@ -249,6 +251,7 @@ constexpr std::array<u32, MaxPixelFormat> block_width_table = {{
249 1, // RG8U 251 1, // RG8U
250 1, // RG8S 252 1, // RG8S
251 1, // RG32UI 253 1, // RG32UI
254 1, // RGBX16F
252 1, // R32UI 255 1, // R32UI
253 8, // ASTC_2D_8X8 256 8, // ASTC_2D_8X8
254 8, // ASTC_2D_8X5 257 8, // ASTC_2D_8X5
@@ -324,6 +327,7 @@ constexpr std::array<u32, MaxPixelFormat> block_height_table = {{
324 1, // RG8U 327 1, // RG8U
325 1, // RG8S 328 1, // RG8S
326 1, // RG32UI 329 1, // RG32UI
330 1, // RGBX16F
327 1, // R32UI 331 1, // R32UI
328 8, // ASTC_2D_8X8 332 8, // ASTC_2D_8X8
329 5, // ASTC_2D_8X5 333 5, // ASTC_2D_8X5
@@ -399,6 +403,7 @@ constexpr std::array<u32, MaxPixelFormat> bpp_table = {{
399 16, // RG8U 403 16, // RG8U
400 16, // RG8S 404 16, // RG8S
401 64, // RG32UI 405 64, // RG32UI
406 64, // RGBX16F
402 32, // R32UI 407 32, // R32UI
403 128, // ASTC_2D_8X8 408 128, // ASTC_2D_8X8
404 128, // ASTC_2D_8X5 409 128, // ASTC_2D_8X5
@@ -489,6 +494,7 @@ constexpr std::array<SurfaceCompression, MaxPixelFormat> compression_type_table
489 SurfaceCompression::None, // RG8U 494 SurfaceCompression::None, // RG8U
490 SurfaceCompression::None, // RG8S 495 SurfaceCompression::None, // RG8S
491 SurfaceCompression::None, // RG32UI 496 SurfaceCompression::None, // RG32UI
497 SurfaceCompression::None, // RGBX16F
492 SurfaceCompression::None, // R32UI 498 SurfaceCompression::None, // R32UI
493 SurfaceCompression::Converted, // ASTC_2D_8X8 499 SurfaceCompression::Converted, // ASTC_2D_8X8
494 SurfaceCompression::Converted, // ASTC_2D_8X5 500 SurfaceCompression::Converted, // ASTC_2D_8X5
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index dc6fa07fc..ff1c1d985 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -66,6 +66,9 @@ add_executable(yuzu
66 configuration/configure_profile_manager.cpp 66 configuration/configure_profile_manager.cpp
67 configuration/configure_profile_manager.h 67 configuration/configure_profile_manager.h
68 configuration/configure_profile_manager.ui 68 configuration/configure_profile_manager.ui
69 configuration/configure_service.cpp
70 configuration/configure_service.h
71 configuration/configure_service.ui
69 configuration/configure_system.cpp 72 configuration/configure_system.cpp
70 configuration/configure_system.h 73 configuration/configure_system.h
71 configuration/configure_system.ui 74 configuration/configure_system.ui
@@ -186,6 +189,10 @@ if (YUZU_USE_QT_WEB_ENGINE)
186 target_compile_definitions(yuzu PRIVATE -DYUZU_USE_QT_WEB_ENGINE) 189 target_compile_definitions(yuzu PRIVATE -DYUZU_USE_QT_WEB_ENGINE)
187endif () 190endif ()
188 191
192if (YUZU_ENABLE_BOXCAT)
193 target_compile_definitions(yuzu PRIVATE -DYUZU_ENABLE_BOXCAT)
194endif ()
195
189if(UNIX AND NOT APPLE) 196if(UNIX AND NOT APPLE)
190 install(TARGETS yuzu RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin") 197 install(TARGETS yuzu RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
191endif() 198endif()
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 92d9fb161..4cb27ddb2 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -525,6 +525,17 @@ void Config::ReadDebuggingValues() {
525 qt_config->endGroup(); 525 qt_config->endGroup();
526} 526}
527 527
528void Config::ReadServiceValues() {
529 qt_config->beginGroup(QStringLiteral("Services"));
530 Settings::values.bcat_backend =
531 ReadSetting(QStringLiteral("bcat_backend"), QStringLiteral("boxcat"))
532 .toString()
533 .toStdString();
534 Settings::values.bcat_boxcat_local =
535 ReadSetting(QStringLiteral("bcat_boxcat_local"), false).toBool();
536 qt_config->endGroup();
537}
538
528void Config::ReadDisabledAddOnValues() { 539void Config::ReadDisabledAddOnValues() {
529 const auto size = qt_config->beginReadArray(QStringLiteral("DisabledAddOns")); 540 const auto size = qt_config->beginReadArray(QStringLiteral("DisabledAddOns"));
530 541
@@ -769,6 +780,7 @@ void Config::ReadValues() {
769 ReadMiscellaneousValues(); 780 ReadMiscellaneousValues();
770 ReadDebuggingValues(); 781 ReadDebuggingValues();
771 ReadWebServiceValues(); 782 ReadWebServiceValues();
783 ReadServiceValues();
772 ReadDisabledAddOnValues(); 784 ReadDisabledAddOnValues();
773 ReadUIValues(); 785 ReadUIValues();
774} 786}
@@ -866,6 +878,7 @@ void Config::SaveValues() {
866 SaveMiscellaneousValues(); 878 SaveMiscellaneousValues();
867 SaveDebuggingValues(); 879 SaveDebuggingValues();
868 SaveWebServiceValues(); 880 SaveWebServiceValues();
881 SaveServiceValues();
869 SaveDisabledAddOnValues(); 882 SaveDisabledAddOnValues();
870 SaveUIValues(); 883 SaveUIValues();
871} 884}
@@ -963,6 +976,14 @@ void Config::SaveDebuggingValues() {
963 qt_config->endGroup(); 976 qt_config->endGroup();
964} 977}
965 978
979void Config::SaveServiceValues() {
980 qt_config->beginGroup(QStringLiteral("Services"));
981 WriteSetting(QStringLiteral("bcat_backend"),
982 QString::fromStdString(Settings::values.bcat_backend), QStringLiteral("null"));
983 WriteSetting(QStringLiteral("bcat_boxcat_local"), Settings::values.bcat_boxcat_local, false);
984 qt_config->endGroup();
985}
986
966void Config::SaveDisabledAddOnValues() { 987void Config::SaveDisabledAddOnValues() {
967 qt_config->beginWriteArray(QStringLiteral("DisabledAddOns")); 988 qt_config->beginWriteArray(QStringLiteral("DisabledAddOns"));
968 989
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index 6b523ecdd..ba6888004 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -42,6 +42,7 @@ private:
42 void ReadCoreValues(); 42 void ReadCoreValues();
43 void ReadDataStorageValues(); 43 void ReadDataStorageValues();
44 void ReadDebuggingValues(); 44 void ReadDebuggingValues();
45 void ReadServiceValues();
45 void ReadDisabledAddOnValues(); 46 void ReadDisabledAddOnValues();
46 void ReadMiscellaneousValues(); 47 void ReadMiscellaneousValues();
47 void ReadPathValues(); 48 void ReadPathValues();
@@ -65,6 +66,7 @@ private:
65 void SaveCoreValues(); 66 void SaveCoreValues();
66 void SaveDataStorageValues(); 67 void SaveDataStorageValues();
67 void SaveDebuggingValues(); 68 void SaveDebuggingValues();
69 void SaveServiceValues();
68 void SaveDisabledAddOnValues(); 70 void SaveDisabledAddOnValues();
69 void SaveMiscellaneousValues(); 71 void SaveMiscellaneousValues();
70 void SavePathValues(); 72 void SavePathValues();
diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui
index 49fadd0ef..372427ae2 100644
--- a/src/yuzu/configuration/configure.ui
+++ b/src/yuzu/configuration/configure.ui
@@ -98,6 +98,11 @@
98 <string>Web</string> 98 <string>Web</string>
99 </attribute> 99 </attribute>
100 </widget> 100 </widget>
101 <widget class="ConfigureService" name="serviceTab">
102 <attribute name="title">
103 <string>Services</string>
104 </attribute>
105 </widget>
101 </widget> 106 </widget>
102 </item> 107 </item>
103 </layout> 108 </layout>
@@ -178,6 +183,12 @@
178 <header>configuration/configure_hotkeys.h</header> 183 <header>configuration/configure_hotkeys.h</header>
179 <container>1</container> 184 <container>1</container>
180 </customwidget> 185 </customwidget>
186 <customwidget>
187 <class>ConfigureService</class>
188 <extends>QWidget</extends>
189 <header>configuration/configure_service.h</header>
190 <container>1</container>
191 </customwidget>
181 </customwidgets> 192 </customwidgets>
182 <resources/> 193 <resources/>
183 <connections> 194 <connections>
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index 7c875ae87..25b2e1b05 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -44,6 +44,7 @@ void ConfigureDialog::ApplyConfiguration() {
44 ui->audioTab->ApplyConfiguration(); 44 ui->audioTab->ApplyConfiguration();
45 ui->debugTab->ApplyConfiguration(); 45 ui->debugTab->ApplyConfiguration();
46 ui->webTab->ApplyConfiguration(); 46 ui->webTab->ApplyConfiguration();
47 ui->serviceTab->ApplyConfiguration();
47 Settings::Apply(); 48 Settings::Apply();
48 Settings::LogSettings(); 49 Settings::LogSettings();
49} 50}
@@ -74,7 +75,8 @@ Q_DECLARE_METATYPE(QList<QWidget*>);
74void ConfigureDialog::PopulateSelectionList() { 75void ConfigureDialog::PopulateSelectionList() {
75 const std::array<std::pair<QString, QList<QWidget*>>, 4> items{ 76 const std::array<std::pair<QString, QList<QWidget*>>, 4> items{
76 {{tr("General"), {ui->generalTab, ui->webTab, ui->debugTab, ui->gameListTab}}, 77 {{tr("General"), {ui->generalTab, ui->webTab, ui->debugTab, ui->gameListTab}},
77 {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->filesystemTab, ui->audioTab}}, 78 {tr("System"),
79 {ui->systemTab, ui->profileManagerTab, ui->serviceTab, ui->filesystemTab, ui->audioTab}},
78 {tr("Graphics"), {ui->graphicsTab}}, 80 {tr("Graphics"), {ui->graphicsTab}},
79 {tr("Controls"), {ui->inputTab, ui->hotkeysTab}}}, 81 {tr("Controls"), {ui->inputTab, ui->hotkeysTab}}},
80 }; 82 };
@@ -108,6 +110,7 @@ void ConfigureDialog::UpdateVisibleTabs() {
108 {ui->webTab, tr("Web")}, 110 {ui->webTab, tr("Web")},
109 {ui->gameListTab, tr("Game List")}, 111 {ui->gameListTab, tr("Game List")},
110 {ui->filesystemTab, tr("Filesystem")}, 112 {ui->filesystemTab, tr("Filesystem")},
113 {ui->serviceTab, tr("Services")},
111 }; 114 };
112 115
113 [[maybe_unused]] const QSignalBlocker blocker(ui->tabWidget); 116 [[maybe_unused]] const QSignalBlocker blocker(ui->tabWidget);
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index b49446be9..98bc9b391 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -2,6 +2,7 @@
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 <QCheckBox>
5#include <QSpinBox> 6#include <QSpinBox>
6#include "core/core.h" 7#include "core/core.h"
7#include "core/settings.h" 8#include "core/settings.h"
diff --git a/src/yuzu/configuration/configure_service.cpp b/src/yuzu/configuration/configure_service.cpp
new file mode 100644
index 000000000..81c9e933f
--- /dev/null
+++ b/src/yuzu/configuration/configure_service.cpp
@@ -0,0 +1,136 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <QGraphicsItem>
6#include <QtConcurrent/QtConcurrent>
7#include "core/hle/service/bcat/backend/boxcat.h"
8#include "core/settings.h"
9#include "ui_configure_service.h"
10#include "yuzu/configuration/configure_service.h"
11
12namespace {
13QString FormatEventStatusString(const Service::BCAT::EventStatus& status) {
14 QString out;
15
16 if (status.header.has_value()) {
17 out += QStringLiteral("<i>%1</i><br>").arg(QString::fromStdString(*status.header));
18 }
19
20 if (status.events.size() == 1) {
21 out += QStringLiteral("%1<br>").arg(QString::fromStdString(status.events.front()));
22 } else {
23 for (const auto& event : status.events) {
24 out += QStringLiteral("- %1<br>").arg(QString::fromStdString(event));
25 }
26 }
27
28 if (status.footer.has_value()) {
29 out += QStringLiteral("<i>%1</i><br>").arg(QString::fromStdString(*status.footer));
30 }
31
32 return out;
33}
34} // Anonymous namespace
35
36ConfigureService::ConfigureService(QWidget* parent)
37 : QWidget(parent), ui(std::make_unique<Ui::ConfigureService>()) {
38 ui->setupUi(this);
39
40 ui->bcat_source->addItem(QStringLiteral("None"));
41 ui->bcat_empty_label->setHidden(true);
42 ui->bcat_empty_header->setHidden(true);
43
44#ifdef YUZU_ENABLE_BOXCAT
45 ui->bcat_source->addItem(QStringLiteral("Boxcat"), QStringLiteral("boxcat"));
46#endif
47
48 connect(ui->bcat_source, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
49 &ConfigureService::OnBCATImplChanged);
50
51 this->SetConfiguration();
52}
53
54ConfigureService::~ConfigureService() = default;
55
56void ConfigureService::ApplyConfiguration() {
57 Settings::values.bcat_backend = ui->bcat_source->currentText().toLower().toStdString();
58}
59
60void ConfigureService::RetranslateUi() {
61 ui->retranslateUi(this);
62}
63
64void ConfigureService::SetConfiguration() {
65 const int index =
66 ui->bcat_source->findData(QString::fromStdString(Settings::values.bcat_backend));
67 ui->bcat_source->setCurrentIndex(index == -1 ? 0 : index);
68}
69
70std::pair<QString, QString> ConfigureService::BCATDownloadEvents() {
71 std::optional<std::string> global;
72 std::map<std::string, Service::BCAT::EventStatus> map;
73 const auto res = Service::BCAT::Boxcat::GetStatus(global, map);
74
75 switch (res) {
76 case Service::BCAT::Boxcat::StatusResult::Offline:
77 return {QString{},
78 tr("The boxcat service is offline or you are not connected to the internet.")};
79 case Service::BCAT::Boxcat::StatusResult::ParseError:
80 return {QString{},
81 tr("There was an error while processing the boxcat event data. Contact the yuzu "
82 "developers.")};
83 case Service::BCAT::Boxcat::StatusResult::BadClientVersion:
84 return {QString{},
85 tr("The version of yuzu you are using is either too new or too old for the server. "
86 "Try updating to the latest official release of yuzu.")};
87 }
88
89 if (map.empty()) {
90 return {QStringLiteral("Current Boxcat Events"),
91 tr("There are currently no events on boxcat.")};
92 }
93
94 QString out;
95
96 if (global.has_value()) {
97 out += QStringLiteral("%1<br>").arg(QString::fromStdString(*global));
98 }
99
100 for (const auto& [key, value] : map) {
101 out += QStringLiteral("%1<b>%2</b><br>%3")
102 .arg(out.isEmpty() ? QString{} : QStringLiteral("<br>"))
103 .arg(QString::fromStdString(key))
104 .arg(FormatEventStatusString(value));
105 }
106 return {QStringLiteral("Current Boxcat Events"), std::move(out)};
107}
108
109void ConfigureService::OnBCATImplChanged() {
110#ifdef YUZU_ENABLE_BOXCAT
111 const auto boxcat = ui->bcat_source->currentText() == QStringLiteral("Boxcat");
112 ui->bcat_empty_header->setHidden(!boxcat);
113 ui->bcat_empty_label->setHidden(!boxcat);
114 ui->bcat_empty_header->setText(QString{});
115 ui->bcat_empty_label->setText(tr("Yuzu is retrieving the latest boxcat status..."));
116
117 if (!boxcat)
118 return;
119
120 const auto future = QtConcurrent::run([this] { return BCATDownloadEvents(); });
121
122 watcher.setFuture(future);
123 connect(&watcher, &QFutureWatcher<std::pair<QString, QString>>::finished, this,
124 [this] { OnUpdateBCATEmptyLabel(watcher.result()); });
125#endif
126}
127
128void ConfigureService::OnUpdateBCATEmptyLabel(std::pair<QString, QString> string) {
129#ifdef YUZU_ENABLE_BOXCAT
130 const auto boxcat = ui->bcat_source->currentText() == QStringLiteral("Boxcat");
131 if (boxcat) {
132 ui->bcat_empty_header->setText(string.first);
133 ui->bcat_empty_label->setText(string.second);
134 }
135#endif
136}
diff --git a/src/yuzu/configuration/configure_service.h b/src/yuzu/configuration/configure_service.h
new file mode 100644
index 000000000..f5c1b703a
--- /dev/null
+++ b/src/yuzu/configuration/configure_service.h
@@ -0,0 +1,34 @@
1// Copyright 2019 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 <memory>
8#include <QFutureWatcher>
9#include <QWidget>
10
11namespace Ui {
12class ConfigureService;
13}
14
15class ConfigureService : public QWidget {
16 Q_OBJECT
17
18public:
19 explicit ConfigureService(QWidget* parent = nullptr);
20 ~ConfigureService() override;
21
22 void ApplyConfiguration();
23 void RetranslateUi();
24
25private:
26 void SetConfiguration();
27
28 std::pair<QString, QString> BCATDownloadEvents();
29 void OnBCATImplChanged();
30 void OnUpdateBCATEmptyLabel(std::pair<QString, QString> string);
31
32 std::unique_ptr<Ui::ConfigureService> ui;
33 QFutureWatcher<std::pair<QString, QString>> watcher{this};
34};
diff --git a/src/yuzu/configuration/configure_service.ui b/src/yuzu/configuration/configure_service.ui
new file mode 100644
index 000000000..9668dd557
--- /dev/null
+++ b/src/yuzu/configuration/configure_service.ui
@@ -0,0 +1,124 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureService</class>
4 <widget class="QWidget" name="ConfigureService">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>433</width>
10 <height>561</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Form</string>
15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout">
17 <item>
18 <layout class="QVBoxLayout" name="verticalLayout_3">
19 <item>
20 <widget class="QGroupBox" name="groupBox">
21 <property name="title">
22 <string>BCAT</string>
23 </property>
24 <layout class="QGridLayout" name="gridLayout">
25 <item row="1" column="1" colspan="2">
26 <widget class="QLabel" name="label_2">
27 <property name="maximumSize">
28 <size>
29 <width>260</width>
30 <height>16777215</height>
31 </size>
32 </property>
33 <property name="text">
34 <string>BCAT is Nintendo's way of sending data to games to engage its community and unlock additional content.</string>
35 </property>
36 <property name="wordWrap">
37 <bool>true</bool>
38 </property>
39 </widget>
40 </item>
41 <item row="0" column="0">
42 <widget class="QLabel" name="label">
43 <property name="maximumSize">
44 <size>
45 <width>16777215</width>
46 <height>16777215</height>
47 </size>
48 </property>
49 <property name="text">
50 <string>BCAT Backend</string>
51 </property>
52 </widget>
53 </item>
54 <item row="3" column="1" colspan="2">
55 <widget class="QLabel" name="bcat_empty_label">
56 <property name="enabled">
57 <bool>true</bool>
58 </property>
59 <property name="maximumSize">
60 <size>
61 <width>260</width>
62 <height>16777215</height>
63 </size>
64 </property>
65 <property name="text">
66 <string/>
67 </property>
68 <property name="alignment">
69 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
70 </property>
71 <property name="wordWrap">
72 <bool>true</bool>
73 </property>
74 </widget>
75 </item>
76 <item row="2" column="1" colspan="2">
77 <widget class="QLabel" name="label_3">
78 <property name="text">
79 <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/help/feature/boxcat&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Learn more about BCAT, Boxcat, and Current Events&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
80 </property>
81 <property name="openExternalLinks">
82 <bool>true</bool>
83 </property>
84 </widget>
85 </item>
86 <item row="0" column="1" colspan="2">
87 <widget class="QComboBox" name="bcat_source"/>
88 </item>
89 <item row="3" column="0">
90 <widget class="QLabel" name="bcat_empty_header">
91 <property name="text">
92 <string/>
93 </property>
94 <property name="alignment">
95 <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
96 </property>
97 <property name="wordWrap">
98 <bool>true</bool>
99 </property>
100 </widget>
101 </item>
102 </layout>
103 </widget>
104 </item>
105 </layout>
106 </item>
107 <item>
108 <spacer name="verticalSpacer">
109 <property name="orientation">
110 <enum>Qt::Vertical</enum>
111 </property>
112 <property name="sizeHint" stdset="0">
113 <size>
114 <width>20</width>
115 <height>40</height>
116 </size>
117 </property>
118 </spacer>
119 </item>
120 </layout>
121 </widget>
122 <resources/>
123 <connections/>
124</ui>
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index f147044d9..2d82df739 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -22,6 +22,8 @@
22#include "core/frontend/applets/general_frontend.h" 22#include "core/frontend/applets/general_frontend.h"
23#include "core/frontend/scope_acquire_window_context.h" 23#include "core/frontend/scope_acquire_window_context.h"
24#include "core/hle/service/acc/profile_manager.h" 24#include "core/hle/service/acc/profile_manager.h"
25#include "core/hle/service/am/applet_ae.h"
26#include "core/hle/service/am/applet_oe.h"
25#include "core/hle/service/am/applets/applets.h" 27#include "core/hle/service/am/applets/applets.h"
26#include "core/hle/service/hid/controllers/npad.h" 28#include "core/hle/service/hid/controllers/npad.h"
27#include "core/hle/service/hid/hid.h" 29#include "core/hle/service/hid/hid.h"
@@ -83,6 +85,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
83#include "core/file_sys/submission_package.h" 85#include "core/file_sys/submission_package.h"
84#include "core/frontend/applets/software_keyboard.h" 86#include "core/frontend/applets/software_keyboard.h"
85#include "core/hle/kernel/process.h" 87#include "core/hle/kernel/process.h"
88#include "core/hle/service/am/am.h"
86#include "core/hle/service/filesystem/filesystem.h" 89#include "core/hle/service/filesystem/filesystem.h"
87#include "core/hle/service/nfp/nfp.h" 90#include "core/hle/service/nfp/nfp.h"
88#include "core/hle/service/sm/sm.h" 91#include "core/hle/service/sm/sm.h"
@@ -1674,6 +1677,11 @@ void GMainWindow::OnStartGame() {
1674} 1677}
1675 1678
1676void GMainWindow::OnPauseGame() { 1679void GMainWindow::OnPauseGame() {
1680 Core::System& system{Core::System::GetInstance()};
1681 if (system.GetExitLock() && !ConfirmForceLockedExit()) {
1682 return;
1683 }
1684
1677 emu_thread->SetRunning(false); 1685 emu_thread->SetRunning(false);
1678 1686
1679 ui.action_Start->setEnabled(true); 1687 ui.action_Start->setEnabled(true);
@@ -1685,6 +1693,11 @@ void GMainWindow::OnPauseGame() {
1685} 1693}
1686 1694
1687void GMainWindow::OnStopGame() { 1695void GMainWindow::OnStopGame() {
1696 Core::System& system{Core::System::GetInstance()};
1697 if (system.GetExitLock() && !ConfirmForceLockedExit()) {
1698 return;
1699 }
1700
1688 ShutdownGame(); 1701 ShutdownGame();
1689} 1702}
1690 1703
@@ -2182,13 +2195,41 @@ bool GMainWindow::ConfirmChangeGame() {
2182 if (emu_thread == nullptr) 2195 if (emu_thread == nullptr)
2183 return true; 2196 return true;
2184 2197
2185 auto answer = QMessageBox::question( 2198 const auto answer = QMessageBox::question(
2186 this, tr("yuzu"), 2199 this, tr("yuzu"),
2187 tr("Are you sure you want to stop the emulation? Any unsaved progress will be lost."), 2200 tr("Are you sure you want to stop the emulation? Any unsaved progress will be lost."),
2188 QMessageBox::Yes | QMessageBox::No, QMessageBox::No); 2201 QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
2189 return answer != QMessageBox::No; 2202 return answer != QMessageBox::No;
2190} 2203}
2191 2204
2205bool GMainWindow::ConfirmForceLockedExit() {
2206 if (emu_thread == nullptr)
2207 return true;
2208
2209 const auto answer =
2210 QMessageBox::question(this, tr("yuzu"),
2211 tr("The currently running application has requested yuzu to not "
2212 "exit.\n\nWould you like to bypass this and exit anyway?"),
2213 QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
2214 return answer != QMessageBox::No;
2215}
2216
2217void GMainWindow::RequestGameExit() {
2218 auto& sm{Core::System::GetInstance().ServiceManager()};
2219 auto applet_oe = sm.GetService<Service::AM::AppletOE>("appletOE");
2220 auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE");
2221 bool has_signalled = false;
2222
2223 if (applet_oe != nullptr) {
2224 applet_oe->GetMessageQueue()->RequestExit();
2225 has_signalled = true;
2226 }
2227
2228 if (applet_ae != nullptr && !has_signalled) {
2229 applet_ae->GetMessageQueue()->RequestExit();
2230 }
2231}
2232
2192void GMainWindow::filterBarSetChecked(bool state) { 2233void GMainWindow::filterBarSetChecked(bool state) {
2193 ui.action_Show_Filter_Bar->setChecked(state); 2234 ui.action_Show_Filter_Bar->setChecked(state);
2194 emit(OnToggleFilterBar()); 2235 emit(OnToggleFilterBar());
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 7d16188cb..e942d1248 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -172,6 +172,8 @@ private:
172 */ 172 */
173 bool ConfirmClose(); 173 bool ConfirmClose();
174 bool ConfirmChangeGame(); 174 bool ConfirmChangeGame();
175 bool ConfirmForceLockedExit();
176 void RequestGameExit();
175 void closeEvent(QCloseEvent* event) override; 177 void closeEvent(QCloseEvent* event) override;
176 178
177private slots: 179private slots:
diff --git a/src/yuzu/uisettings.cpp b/src/yuzu/uisettings.cpp
index 7f7d247a3..43bad9678 100644
--- a/src/yuzu/uisettings.cpp
+++ b/src/yuzu/uisettings.cpp
@@ -9,6 +9,8 @@ namespace UISettings {
9const Themes themes{{ 9const Themes themes{{
10 {"Default", "default"}, 10 {"Default", "default"},
11 {"Dark", "qdarkstyle"}, 11 {"Dark", "qdarkstyle"},
12 {"Colorful", "colorful"},
13 {"Colorful Dark", "colorful_dark"},
12}}; 14}};
13 15
14Values values = {}; 16Values values = {};
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index c57290006..a8eaf5f8c 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -24,7 +24,7 @@ struct Shortcut {
24 ContextualShortcut shortcut; 24 ContextualShortcut shortcut;
25}; 25};
26 26
27using Themes = std::array<std::pair<const char*, const char*>, 2>; 27using Themes = std::array<std::pair<const char*, const char*>, 4>;
28extern const Themes themes; 28extern const Themes themes;
29 29
30struct GameDir { 30struct GameDir {
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index d82438502..1a812cb87 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -433,6 +433,11 @@ void Config::ReadValues() {
433 sdl2_config->Get("WebService", "web_api_url", "https://api.yuzu-emu.org"); 433 sdl2_config->Get("WebService", "web_api_url", "https://api.yuzu-emu.org");
434 Settings::values.yuzu_username = sdl2_config->Get("WebService", "yuzu_username", ""); 434 Settings::values.yuzu_username = sdl2_config->Get("WebService", "yuzu_username", "");
435 Settings::values.yuzu_token = sdl2_config->Get("WebService", "yuzu_token", ""); 435 Settings::values.yuzu_token = sdl2_config->Get("WebService", "yuzu_token", "");
436
437 // Services
438 Settings::values.bcat_backend = sdl2_config->Get("Services", "bcat_backend", "boxcat");
439 Settings::values.bcat_boxcat_local =
440 sdl2_config->GetBoolean("Services", "bcat_boxcat_local", false);
436} 441}
437 442
438void Config::Reload() { 443void Config::Reload() {
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index a6171c3ed..8d18a4a5a 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -251,6 +251,11 @@ web_api_url = https://api.yuzu-emu.org
251yuzu_username = 251yuzu_username =
252yuzu_token = 252yuzu_token =
253 253
254[Services]
255# The name of the backend to use for BCAT
256# If this is set to 'boxcat' boxcat will be used, otherwise a null implementation will be used
257bcat_backend =
258
254[AddOns] 259[AddOns]
255# Used to disable add-ons 260# Used to disable add-ons
256# List of title IDs of games that will have add-ons disabled (separated by '|'): 261# List of title IDs of games that will have add-ons disabled (separated by '|'):