Compare commits

..

11 Commits

Author SHA1 Message Date
ReinUsesLisp
84b6fbc340 shader/decode: Remove unused variables 2019-08-29 22:51:10 +00:00
ReinUsesLisp
edcddcfcd1 buffer_cache: Add missing include 2019-08-29 22:51:10 +00:00
ReinUsesLisp
ad7761ded7 gl_texture_cache: Add missing override 2019-08-29 22:51:10 +00:00
ReinUsesLisp
54cc4fedce kepler_memory: Remove unused MemoryManager reference 2019-08-29 22:51:10 +00:00
ReinUsesLisp
54057734c2 buffer_cache: Silent -Wreorder warnings 2019-08-29 22:51:10 +00:00
ReinUsesLisp
d0e9f5aef2 video_core: Silent unused variable warnings 2019-08-29 22:51:10 +00:00
ReinUsesLisp
05acec8080 fermi_2d: Remove unused MemoryManager reference 2019-08-29 22:51:10 +00:00
ReinUsesLisp
5caa84d080 video_core/gpu: Sort member declaration order to silent -Wreorder warning 2019-08-29 22:51:10 +00:00
ReinUsesLisp
041a8da6e7 maxwell_dma: Remove unused rasterizer reference 2019-08-29 22:51:10 +00:00
ReinUsesLisp
8afa889a05 rasterizer_interface: Add missing documentation commentary 2019-08-29 22:51:10 +00:00
ReinUsesLisp
07d8a23abb texture_cache/surface_params: Remove unused local variable 2019-08-29 22:51:00 +00:00
351 changed files with 3482 additions and 11710 deletions

View File

@@ -5,11 +5,10 @@ cd /yuzu
ccache -s
mkdir build || true && cd build
cmake .. -G Ninja -DDISPLAY_VERSION=$1 -DYUZU_USE_BUNDLED_UNICORN=ON -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON
cmake .. -G Ninja -DYUZU_USE_BUNDLED_UNICORN=ON -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON
ninja
ccache -s
# Ignore zlib's tests, since they aren't gated behind a CMake option.
ctest -VV -E "(example|example64)" -C Release
ctest -VV -C Release

View File

@@ -2,4 +2,4 @@
mkdir -p "ccache" || true
chmod a+x ./.ci/scripts/linux/docker.sh
docker run -e ENABLE_COMPATIBILITY_REPORTING -e CCACHE_DIR=/yuzu/ccache -v $(pwd):/yuzu yuzuemu/build-environments:linux-fresh /bin/bash /yuzu/.ci/scripts/linux/docker.sh $1
docker run -e ENABLE_COMPATIBILITY_REPORTING -e CCACHE_DIR=/yuzu/ccache -v $(pwd):/yuzu yuzuemu/build-environments:linux-fresh /bin/bash /yuzu/.ci/scripts/linux/docker.sh

View File

@@ -1,45 +0,0 @@
# Download all pull requests as patches that match a specific label
# Usage: python download-patches-by-label.py <Label to Match> <Root Path Folder to DL to>
import requests, sys, json, shutil, subprocess, os, traceback
org = os.getenv("PRIVATEMERGEORG", "yuzu-emu")
repo = os.getenv("PRIVATEMERGEREPO", "yuzu-private")
tagline = sys.argv[3]
user = sys.argv[1]
dl_list = {}
TAG_NAME = sys.argv[2]
def check_individual(repo_id, pr_id):
url = 'https://%sdev.azure.com/%s/%s/_apis/git/repositories/%s/pullRequests/%s/labels?api-version=5.1-preview.1' % (user, org, repo, repo_id, pr_id)
response = requests.get(url)
if (response.ok):
try:
js = response.json()
return any(tag.get('name') == TAG_NAME for tag in js['value'])
except:
return False
return False
def merge_pr(pn, ref):
print("Matched PR# %s" % pn)
print(subprocess.check_output(["git", "fetch", "https://%sdev.azure.com/%s/_git/%s" % (user, org, repo), ref, "-f"]))
print(subprocess.check_output(["git", "merge", "--squash", 'origin/' + ref.replace('refs/heads/','')]))
print(subprocess.check_output(["git", "commit", "-m\"Merge %s PR %s\"" % (tagline, pn)]))
def main():
url = 'https://%sdev.azure.com/%s/%s/_apis/git/pullrequests?api-version=5.1' % (user, org, repo)
response = requests.get(url)
if (response.ok):
js = response.json()
tagged_prs = filter(lambda pr: check_individual(pr['repository']['id'], pr['pullRequestId']), js['value'])
map(lambda pr: merge_pr(pr['pullRequestId'], pr['sourceRefName']), tagged_prs)
if __name__ == '__main__':
try:
main()
except:
traceback.print_exc(file=sys.stdout)
sys.exit(-1)

View File

@@ -1,9 +1,7 @@
# Download all pull requests as patches that match a specific label
# Usage: python download-patches-by-label.py <Label to Match> <Root Path Folder to DL to>
import requests, sys, json, urllib3.request, shutil, subprocess, os
tagline = sys.argv[2]
import requests, sys, json, urllib3.request, shutil, subprocess
http = urllib3.PoolManager()
dl_list = {}
@@ -14,23 +12,17 @@ def check_individual(labels):
return True
return False
def do_page(page):
url = 'https://api.github.com/repos/yuzu-emu/yuzu/pulls?page=%s' % page
try:
url = 'https://api.github.com/repos/yuzu-emu/yuzu/pulls'
response = requests.get(url)
if (response.ok):
j = json.loads(response.content)
if j == []:
return
for pr in j:
if (check_individual(pr["labels"])):
pn = pr["number"]
print("Matched PR# %s" % pn)
print(subprocess.check_output(["git", "fetch", "https://github.com/yuzu-emu/yuzu.git", "pull/%s/head:pr-%s" % (pn, pn), "-f"]))
print(subprocess.check_output(["git", "merge", "--squash", "pr-%s" % pn]))
print(subprocess.check_output(["git", "commit", "-m\"Merge %s PR %s\"" % (tagline, pn)]))
try:
for i in range(1,30):
do_page(i)
print(subprocess.check_output(["git", "commit", "-m\"Merge PR %s\"" % pn]))
except:
sys.exit(-1)

View File

@@ -13,7 +13,7 @@ echo '' >> /bin/cmd
chmod +x /bin/cmd
mkdir build || true && cd build
cmake .. -G Ninja -DDISPLAY_VERSION=$1 -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_UNICORN=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release
cmake .. -G Ninja -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_UNICORN=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release
ninja
# Clean up the dirty hacks

View File

@@ -2,4 +2,4 @@
mkdir -p "ccache" || true
chmod a+x ./.ci/scripts/windows/docker.sh
docker run -e CCACHE_DIR=/yuzu/ccache -v $(pwd):/yuzu yuzuemu/build-environments:linux-mingw /bin/bash -ex /yuzu/.ci/scripts/windows/docker.sh $1
docker run -e CCACHE_DIR=/yuzu/ccache -v $(pwd):/yuzu yuzuemu/build-environments:linux-mingw /bin/bash -ex /yuzu/.ci/scripts/windows/docker.sh

View File

@@ -1,32 +0,0 @@
$GITDATE = $(git show -s --date=short --format='%ad') -replace "-",""
$GITREV = $(git show -s --format='%h')
$RELEASE_DIST = "yuzu-windows-msvc"
$MSVC_BUILD_ZIP = "yuzu-windows-msvc-$GITDATE-$GITREV.zip" -replace " ", ""
$MSVC_BUILD_PDB = "yuzu-windows-msvc-$GITDATE-$GITREV-debugsymbols.zip" -replace " ", ""
$MSVC_SEVENZIP = "yuzu-windows-msvc-$GITDATE-$GITREV.7z" -replace " ", ""
$env:BUILD_ZIP = $MSVC_BUILD_ZIP
$env:BUILD_SYMBOLS = $MSVC_BUILD_PDB
$env:BUILD_UPDATE = $MSVC_SEVENZIP
$BUILD_DIR = ".\build\bin\Release"
mkdir pdb
Get-ChildItem "$BUILD_DIR\" -Recurse -Filter "*.pdb" | Copy-Item -destination .\pdb
7z a -tzip $MSVC_BUILD_PDB .\pdb\*.pdb
rm "$BUILD_DIR\*.pdb"
mkdir $RELEASE_DIST
mkdir "artifacts"
Copy-Item "$BUILD_DIR\*" -Destination $RELEASE_DIST -Recurse
rm "$RELEASE_DIST\*.exe"
Get-ChildItem "$BUILD_DIR" -Recurse -Filter "yuzu*.exe" | Copy-Item -destination $RELEASE_DIST
Get-ChildItem "$BUILD_DIR" -Recurse -Filter "QtWebEngineProcess*.exe" | Copy-Item -destination $RELEASE_DIST
Copy-Item .\license.txt -Destination $RELEASE_DIST
Copy-Item .\README.md -Destination $RELEASE_DIST
7z a -tzip $MSVC_BUILD_ZIP $RELEASE_DIST\*
7z a $MSVC_SEVENZIP $RELEASE_DIST
Get-ChildItem . -Filter "*.zip" | Copy-Item -destination "artifacts"
Get-ChildItem . -Filter "*.7z" | Copy-Item -destination "artifacts"

View File

@@ -1,5 +0,0 @@
steps:
- script: mkdir artifacts || echo 'X' > artifacts/T1.txt
- publish: artifacts
artifact: 'yuzu-$(BuildName)-$(BuildSuffix)'
displayName: 'Upload Artifacts'

View File

@@ -1,22 +0,0 @@
parameters:
artifactSource: 'true'
cache: 'false'
version: ''
steps:
- script: mkdir build && cd build && cmake -G "Visual Studio 15 2017 Win64" --config Release -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_BUNDLED_UNICORN=1 -DYUZU_USE_QT_WEB_ENGINE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON -DDISPLAY_VERSION=${{ parameters['version'] }} .. && cd ..
displayName: 'Configure CMake'
- task: MSBuild@1
displayName: 'Build'
inputs:
solution: 'build/yuzu.sln'
maximumCpuCount: true
configuration: release
- task: PowerShell@2
displayName: 'Package Artifacts'
inputs:
targetType: 'filePath'
filePath: './.ci/scripts/windows/upload.ps1'
- publish: artifacts
artifact: 'yuzu-$(BuildName)-windows-msvc'
displayName: 'Upload Artifacts'

View File

@@ -1,20 +1,20 @@
parameters:
artifactSource: 'true'
cache: 'false'
version: ''
steps:
- task: DockerInstaller@0
displayName: 'Prepare Environment'
inputs:
dockerVersion: '17.09.0-ce'
- task: CacheBeta@0
displayName: 'Cache Build System'
inputs:
key: yuzu-v1-$(BuildName)-$(BuildSuffix)-$(CacheSuffix)
path: $(System.DefaultWorkingDirectory)/ccache
cacheHitVar: CACHE_RESTORED
- script: chmod a+x ./.ci/scripts/$(ScriptFolder)/exec.sh && ./.ci/scripts/$(ScriptFolder)/exec.sh ${{ parameters['version'] }}
- ${{ if eq(parameters.cache, 'true') }}:
- task: CacheBeta@0
displayName: 'Cache Build System'
inputs:
key: yuzu-v1-$(BuildName)-$(BuildSuffix)-$(CacheSuffix)
path: $(System.DefaultWorkingDirectory)/ccache
cacheHitVar: CACHE_RESTORED
- script: chmod a+x ./.ci/scripts/$(ScriptFolder)/exec.sh && ./.ci/scripts/$(ScriptFolder)/exec.sh
displayName: 'Build'
- script: chmod a+x ./.ci/scripts/$(ScriptFolder)/upload.sh && RELEASE_NAME=$(BuildName) ./.ci/scripts/$(ScriptFolder)/upload.sh
displayName: 'Package Artifacts'

View File

@@ -1,6 +1,3 @@
parameters:
version: ''
jobs:
- job: build
displayName: 'standard'
@@ -23,5 +20,4 @@ jobs:
- template: ./build-single.yml
parameters:
artifactSource: 'false'
cache: $(parameters.cache)
version: $(parameters.version)
cache: $(parameters.cache)

View File

@@ -1,6 +1,3 @@
parameters:
version: ''
jobs:
- job: build_test
displayName: 'testing'
@@ -13,7 +10,7 @@ jobs:
BuildSuffix: 'windows-testing'
ScriptFolder: 'windows'
steps:
- script: sudo apt-get update && sudo apt-get --only-upgrade -y install python3-pip && pip install requests urllib3
- script: sudo apt upgrade python3-pip && pip install requests urllib3
displayName: 'Prepare Environment'
- task: PythonScript@0
condition: eq(variables['Build.Reason'], 'PullRequest')
@@ -34,4 +31,3 @@ jobs:
parameters:
artifactSource: 'false'
cache: 'false'
version: $(parameters.version)

View File

@@ -1,47 +0,0 @@
jobs:
- job: merge
displayName: 'pull requests'
steps:
- checkout: self
submodules: recursive
- template: ./mergebot-private.yml
parameters:
matchLabel: '$(BuildName)-merge'
matchLabelPublic: '$(PublicBuildName)-merge'
- task: ArchiveFiles@2
displayName: 'Package Source'
inputs:
rootFolderOrFile: '$(System.DefaultWorkingDirectory)'
includeRootFolder: false
archiveType: '7z'
archiveFile: '$(Build.ArtifactStagingDirectory)/yuzu-$(BuildName)-source.7z'
- task: PublishPipelineArtifact@1
displayName: 'Upload Artifacts'
inputs:
targetPath: '$(Build.ArtifactStagingDirectory)/yuzu-$(BuildName)-source.7z'
artifact: 'yuzu-$(BuildName)-source'
replaceExistingArchive: true
- job: upload_source
displayName: 'upload'
dependsOn: merge
steps:
- template: ./sync-source.yml
parameters:
artifactSource: 'true'
needSubmodules: 'true'
- script: chmod a+x $(System.DefaultWorkingDirectory)/.ci/scripts/merge/yuzubot-git-config.sh && $(System.DefaultWorkingDirectory)/.ci/scripts/merge/yuzubot-git-config.sh
displayName: 'Apply Git Configuration'
- script: git tag -a $(BuildName)-$(Build.BuildId) -m "yuzu $(BuildName) $(Build.BuildNumber) $(Build.DefinitionName)"
displayName: 'Tag Source'
- script: git remote add other $(GitRepoPushChangesURL)
displayName: 'Register Repository'
- script: git push --follow-tags --force other HEAD:$(GitPushBranch)
displayName: 'Update Code'
- script: git rev-list -n 1 $(BuildName)-$(Build.BuildId) > $(Build.ArtifactStagingDirectory)/tag-commit.sha
displayName: 'Calculate Release Point'
- task: PublishPipelineArtifact@1
displayName: 'Upload Release Point'
inputs:
targetPath: '$(Build.ArtifactStagingDirectory)/tag-commit.sha'
artifact: 'yuzu-$(BuildName)-release-point'
replaceExistingArchive: true

View File

@@ -1,30 +0,0 @@
parameters:
matchLabel: 'dummy-merge'
matchLabelPublic: 'dummy-merge'
steps:
- script: mkdir $(System.DefaultWorkingDirectory)/patches && pip install requests urllib3
displayName: 'Prepare Environment'
- script: chmod a+x $(System.DefaultWorkingDirectory)/.ci/scripts/merge/yuzubot-git-config.sh && $(System.DefaultWorkingDirectory)/.ci/scripts/merge/yuzubot-git-config.sh
displayName: 'Apply Git Configuration'
- task: PythonScript@0
displayName: 'Discover, Download, and Apply Patches (Mainline)'
inputs:
scriptSource: 'filePath'
scriptPath: '.ci/scripts/merge/apply-patches-by-label.py'
arguments: '${{ parameters.matchLabelPublic }} $(MergeTaglinePublic) patches-public'
workingDirectory: '$(System.DefaultWorkingDirectory)'
- task: PythonScript@0
displayName: 'Discover, Download, and Apply Patches (Patreon Public)'
inputs:
scriptSource: 'filePath'
scriptPath: '.ci/scripts/merge/apply-patches-by-label.py'
arguments: '${{ parameters.matchLabel }} "$(MergeTaglinePrivate) Public" patches-mixed-public'
workingDirectory: '$(System.DefaultWorkingDirectory)'
- task: PythonScript@0
displayName: 'Discover, Download, and Apply Patches (Patreon Private)'
inputs:
scriptSource: 'filePath'
scriptPath: '.ci/scripts/merge/apply-patches-by-label-private.py'
arguments: '$(PrivateMergeUser) ${{ parameters.matchLabel }} "$(MergeTaglinePrivate) Private" patches-private'
workingDirectory: '$(System.DefaultWorkingDirectory)'

View File

@@ -11,5 +11,5 @@ steps:
inputs:
scriptSource: 'filePath'
scriptPath: '.ci/scripts/merge/apply-patches-by-label.py'
arguments: '${{ parameters.matchLabel }} Tagged patches'
arguments: '${{ parameters.matchLabel }} patches'
workingDirectory: '$(System.DefaultWorkingDirectory)'

View File

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

View File

@@ -1,11 +0,0 @@
steps:
- template: ./release-download.yml
- task: GitHubRelease@0
inputs:
action: 'create'
title: 'yuzu $(BuildName) #$(Build.BuildId)'
assets: '$(Build.ArtifactStagingDirectory)/*'
gitHubConnection: $(GitHubReleaseConnectionName)
repositoryName: '$(Build.Repository.Name)'
target: '$(Build.SourceVersion)'
tagSource: 'auto'

View File

@@ -1,10 +0,0 @@
steps:
- template: ./release-download.yml
- task: UniversalPackages@0
displayName: Publish Artifacts
inputs:
command: publish
publishDirectory: '$(Build.ArtifactStagingDirectory)'
vstsFeedPublish: 'yuzu-$(BuildName)'
vstsFeedPackagePublish: 'main'
packagePublishDescription: 'Yuzu Windows and Linux Executable Packages'

View File

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

View File

@@ -1,68 +0,0 @@
trigger:
- master
variables:
DisplayVersion: $[counter(variables['DisplayPrefix'], 1)]
stages:
- stage: format
displayName: 'format'
jobs:
- job: format
displayName: 'clang'
pool:
vmImage: ubuntu-latest
steps:
- template: ./templates/format-check.yml
- stage: build
dependsOn: format
displayName: 'build'
jobs:
- job: build
displayName: 'standard'
pool:
vmImage: ubuntu-latest
strategy:
maxParallel: 10
matrix:
linux:
BuildSuffix: 'linux'
ScriptFolder: 'linux'
steps:
- template: ./templates/sync-source.yml
parameters:
artifactSource: $(parameters.artifactSource)
needSubmodules: 'true'
- template: ./templates/build-single.yml
parameters:
artifactSource: 'false'
cache: 'true'
version: $(DisplayVersion)
- stage: build_win
dependsOn: format
displayName: 'build-windows'
jobs:
- job: build
displayName: 'msvc'
pool:
vmImage: vs2017-win2016
steps:
- template: ./templates/sync-source.yml
parameters:
artifactSource: $(parameters.artifactSource)
needSubmodules: 'true'
- template: ./templates/build-msvc.yml
parameters:
artifactSource: 'false'
cache: 'true'
version: $(DisplayVersion)
- stage: release
displayName: 'Release'
dependsOn:
- build
- build_win
jobs:
- job: github
displayName: 'GitHub Release'
steps:
- template: ./templates/release-github.yml

25
.ci/yuzu-mainline.yml Normal file
View File

@@ -0,0 +1,25 @@
trigger:
- master
stages:
- stage: merge
displayName: 'merge'
jobs:
- template: ./templates/merge.yml
- stage: format
dependsOn: merge
displayName: 'format'
jobs:
- job: format
displayName: 'clang'
pool:
vmImage: ubuntu-latest
steps:
- template: ./templates/format-check.yml
- stage: build
displayName: 'build'
dependsOn: format
jobs:
- template: ./templates/build-standard.yml
parameters:
cache: 'true'

View File

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

View File

@@ -1,34 +0,0 @@
trigger:
- master
variables:
DisplayVersion: $[counter(variables['DisplayPrefix'], 1)]
stages:
- stage: format
displayName: 'format'
jobs:
- job: format
displayName: 'clang'
pool:
vmImage: ubuntu-latest
steps:
- template: ./templates/format-check.yml
- stage: build
dependsOn: format
displayName: 'build'
jobs:
- job: build
displayName: 'windows-msvc'
pool:
vmImage: vs2017-win2016
steps:
- template: ./templates/sync-source.yml
parameters:
artifactSource: $(parameters.artifactSource)
needSubmodules: 'true'
- template: ./templates/build-msvc.yml
parameters:
artifactSource: 'false'
cache: $(parameters.cache)
version: $(DisplayVersion)

19
.ci/yuzu-patreon.yml Normal file
View File

@@ -0,0 +1,19 @@
# Starter pipeline
# Start with a minimal pipeline that you can customize to build and deploy your code.
# Add steps that build, run tests, deploy, and more:
# https://aka.ms/yaml
trigger:
- master
pool:
vmImage: 'ubuntu-latest'
steps:
- script: echo Hello, world!
displayName: 'Run a one-line script'
- script: |
echo Add other tasks to build, test, and deploy your project.
echo See https://aka.ms/yaml
displayName: 'Run a multi-line script'

6
.gitmodules vendored
View File

@@ -46,9 +46,3 @@
[submodule "sirit"]
path = externals/sirit
url = https://github.com/ReinUsesLisp/sirit
[submodule "libzip"]
path = externals/libzip
url = https://github.com/DarkLordZach/libzip
[submodule "zlib"]
path = externals/zlib
url = https://github.com/madler/zlib

View File

@@ -21,8 +21,6 @@ option(YUZU_USE_BUNDLED_UNICORN "Build/Download bundled Unicorn" ON)
option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OFF)
option(YUZU_ENABLE_BOXCAT "Enable the Boxcat service, a yuzu high-level implementation of BCAT" ON)
option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
option(ENABLE_VULKAN "Enables Vulkan backend" ON)

View File

@@ -1,15 +1,14 @@
yuzu emulator
=============
[![Travis CI Build Status](https://travis-ci.org/yuzu-emu/yuzu.svg?branch=master)](https://travis-ci.org/yuzu-emu/yuzu)
[![AppVeyor CI Build Status](https://ci.appveyor.com/api/projects/status/77k97svb2usreu68?svg=true)](https://ci.appveyor.com/project/bunnei/yuzu)
[![Azure Mainline CI Build Status](https://dev.azure.com/yuzu-emu/yuzu/_apis/build/status/yuzu%20mainline?branchName=master)](https://dev.azure.com/yuzu-emu/yuzu/)
yuzu is an experimental open-source emulator for the Nintendo Switch from the creators of [Citra](https://citra-emu.org/).
It is written in C++ with portability in mind, with builds actively maintained for Windows and Linux. The emulator is capable of running several commercial games.
It is written in C++ with portability in mind, with builds actively maintained for Windows, Linux and macOS. The emulator is currently only useful for homebrew development and research purposes.
yuzu only emulates a subset of Switch hardware and therefore most commercial games **do not** run at full speed or are not fully functional.
Do you want to check which games are compatible and which ones are not? Please visit our [Compatibility page](https://yuzu-emu.org/game/)!
yuzu only emulates a subset of Switch hardware and therefore is generally only useful for running/debugging homebrew applications. yuzu can boot some games, to varying degrees of success.
yuzu is licensed under the GPLv2 (or any later version). Refer to the license.txt file included.

31
dist/license.md vendored
View File

@@ -1,31 +0,0 @@
The icons in this folder and its subfolders have the following licenses:
Icon Name | License | Origin/Author
--- | --- | ---
qt_themes/default/icons/16x16/checked.png | Free for non-commercial use
qt_themes/default/icons/16x16/failed.png | Free for non-commercial use
qt_themes/default/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/48x48/bad_folder.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/48x48/chip.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/48x48/folder.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/48x48/plus.png | CC0 1.0 | Designed by BreadFish64 from the Citra team
qt_themes/default/icons/48x48/sd_card.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/qdarkstyle/icons/16x16/checked.png | Free for non-commercial use
qt_themes/qdarkstyle/icons/16x16/failed.png | Free for non-commercial use
qt_themes/qdarkstyle/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/qdarkstyle/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/qdarkstyle/icons/48x48/bad_folder.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/qdarkstyle/icons/48x48/chip.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/qdarkstyle/icons/48x48/folder.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/qdarkstyle/icons/48x48/plus.png | CC0 1.0 | Designed by BreadFish64 from the Citra team
qt_themes/qdarkstyle/icons/48x48/sd_card.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/colorful/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/colorful/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/colorful/icons/48x48/bad_folder.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/colorful/icons/48x48/chip.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/colorful/icons/48x48/folder.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/colorful/icons/48x48/plus.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/colorful/icons/48x48/sd_card.png | CC BY-ND 3.0 | https://icons8.com
<!-- TODO: Add the license of the yuzu icon -->

Binary file not shown.

Before

Width:  |  Height:  |  Size: 330 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 582 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 460 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 496 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 680 B

View File

@@ -1,14 +0,0 @@
[Icon Theme]
Name=colorful
Comment=Colorful theme
Inherits=default
Directories=16x16,48x48,256x256
[16x16]
Size=16
[48x48]
Size=48
[256x256]
Size=256

View File

@@ -1,15 +0,0 @@
<RCC>
<qresource prefix="icons/colorful">
<file alias="index.theme">icons/index.theme</file>
<file alias="16x16/lock.png">icons/16x16/lock.png</file>
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
<file alias="48x48/chip.png">icons/48x48/chip.png</file>
<file alias="48x48/folder.png">icons/48x48/folder.png</file>
<file alias="48x48/plus.png">icons/48x48/plus.png</file>
<file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file>
<file alias="256x256/plus_folder.png">icons/256x256/plus_folder.png</file>
</qresource>
<qresource prefix="colorful">
<file>style.qss</file>
</qresource>
</RCC>

View File

@@ -1,4 +0,0 @@
/*
This file is intentionally left blank.
We do not want to apply any stylesheet for colorful, only icons.
*/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 401 B

View File

@@ -1,8 +0,0 @@
[Icon Theme]
Name=colorful_dark
Comment=Colorful theme (Dark style)
Inherits=default
Directories=16x16
[16x16]
Size=16

View File

@@ -1,57 +0,0 @@
<RCC>
<qresource prefix="icons/colorful_dark">
<file alias="index.theme">icons/index.theme</file>
<file alias="16x16/lock.png">icons/16x16/lock.png</file>
<file alias="48x48/bad_folder.png">../colorful/icons/48x48/bad_folder.png</file>
<file alias="48x48/chip.png">../colorful/icons/48x48/chip.png</file>
<file alias="48x48/folder.png">../colorful/icons/48x48/folder.png</file>
<file alias="48x48/plus.png">../colorful/icons/48x48/plus.png</file>
<file alias="48x48/sd_card.png">../colorful/icons/48x48/sd_card.png</file>
<file alias="256x256/plus_folder.png">../colorful/icons/256x256/plus_folder.png</file>
</qresource>
<qresource prefix="qss_icons">
<file alias="rc/up_arrow_disabled.png">../qdarkstyle/rc/up_arrow_disabled.png</file>
<file alias="rc/Hmovetoolbar.png">../qdarkstyle/rc/Hmovetoolbar.png</file>
<file alias="rc/stylesheet-branch-end.png">../qdarkstyle/rc/stylesheet-branch-end.png</file>
<file alias="rc/branch_closed-on.png">../qdarkstyle/rc/branch_closed-on.png</file>
<file alias="rc/stylesheet-vline.png">../qdarkstyle/rc/stylesheet-vline.png</file>
<file alias="rc/branch_closed.png">../qdarkstyle/rc/branch_closed.png</file>
<file alias="rc/branch_open-on.png">../qdarkstyle/rc/branch_open-on.png</file>
<file alias="rc/transparent.png">../qdarkstyle/rc/transparent.png</file>
<file alias="rc/right_arrow_disabled.png">../qdarkstyle/rc/right_arrow_disabled.png</file>
<file alias="rc/sizegrip.png">../qdarkstyle/rc/sizegrip.png</file>
<file alias="rc/close.png">../qdarkstyle/rc/close.png</file>
<file alias="rc/close-hover.png">../qdarkstyle/rc/close-hover.png</file>
<file alias="rc/close-pressed.png">../qdarkstyle/rc/close-pressed.png</file>
<file alias="rc/down_arrow.png">../qdarkstyle/rc/down_arrow.png</file>
<file alias="rc/Vmovetoolbar.png">../qdarkstyle/rc/Vmovetoolbar.png</file>
<file alias="rc/left_arrow.png">../qdarkstyle/rc/left_arrow.png</file>
<file alias="rc/stylesheet-branch-more.png">../qdarkstyle/rc/stylesheet-branch-more.png</file>
<file alias="rc/up_arrow.png">../qdarkstyle/rc/up_arrow.png</file>
<file alias="rc/right_arrow.png">../qdarkstyle/rc/right_arrow.png</file>
<file alias="rc/left_arrow_disabled.png">../qdarkstyle/rc/left_arrow_disabled.png</file>
<file alias="rc/Hsepartoolbar.png">../qdarkstyle/rc/Hsepartoolbar.png</file>
<file alias="rc/branch_open.png">../qdarkstyle/rc/branch_open.png</file>
<file alias="rc/Vsepartoolbar.png">../qdarkstyle/rc/Vsepartoolbar.png</file>
<file alias="rc/down_arrow_disabled.png">../qdarkstyle/rc/down_arrow_disabled.png</file>
<file alias="rc/undock.png">../qdarkstyle/rc/undock.png</file>
<file alias="rc/checkbox_checked_disabled.png">../qdarkstyle/rc/checkbox_checked_disabled.png</file>
<file alias="rc/checkbox_checked_focus.png">../qdarkstyle/rc/checkbox_checked_focus.png</file>
<file alias="rc/checkbox_checked.png">../qdarkstyle/rc/checkbox_checked.png</file>
<file alias="rc/checkbox_indeterminate.png">../qdarkstyle/rc/checkbox_indeterminate.png</file>
<file alias="rc/checkbox_indeterminate_focus.png">../qdarkstyle/rc/checkbox_indeterminate_focus.png</file>
<file alias="rc/checkbox_unchecked_disabled.png">../qdarkstyle/rc/checkbox_unchecked_disabled.png</file>
<file alias="rc/checkbox_unchecked_focus.png">../qdarkstyle/rc/checkbox_unchecked_focus.png</file>
<file alias="rc/checkbox_unchecked.png">../qdarkstyle/rc/checkbox_unchecked.png</file>
<file alias="rc/radio_checked_disabled.png">../qdarkstyle/rc/radio_checked_disabled.png</file>
<file alias="rc/radio_checked_focus.png">../qdarkstyle/rc/radio_checked_focus.png</file>
<file alias="rc/radio_checked.png">../qdarkstyle/rc/radio_checked.png</file>
<file alias="rc/radio_unchecked_disabled.png">../qdarkstyle/rc/radio_unchecked_disabled.png</file>
<file alias="rc/radio_unchecked_focus.png">../qdarkstyle/rc/radio_unchecked_focus.png</file>
<file alias="rc/radio_unchecked.png">../qdarkstyle/rc/radio_unchecked.png</file>
</qresource>
<qresource prefix="colorful_dark">
<file alias="style.qss">../qdarkstyle/style.qss</file>
</qresource>
</RCC>

View File

@@ -5,21 +5,7 @@
<file alias="16x16/checked.png">icons/16x16/checked.png</file>
<file alias="16x16/failed.png">icons/16x16/failed.png</file>
<file alias="16x16/lock.png">icons/16x16/lock.png</file>
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
<file alias="48x48/chip.png">icons/48x48/chip.png</file>
<file alias="48x48/folder.png">icons/48x48/folder.png</file>
<file alias="48x48/plus.png">icons/48x48/plus.png</file>
<file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file>
<file alias="256x256/yuzu.png">icons/256x256/yuzu.png</file>
<file alias="256x256/plus_folder.png">icons/256x256/plus_folder.png</file>
</qresource>
</RCC>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 279 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 410 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 316 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 614 B

View File

@@ -1,13 +1,10 @@
[Icon Theme]
Name=default
Comment=default theme
Directories=16x16,48x48,256x256
Directories=16x16,256x256
[16x16]
Size=16
[48x48]
Size=48
[256x256]
Size=256

Binary file not shown.

Before

Width:  |  Height:  |  Size: 304 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 542 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 339 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 676 B

View File

@@ -2,13 +2,10 @@
Name=qdarkstyle
Comment=dark theme
Inherits=default
Directories=16x16,48x48,256x256
Directories=16x16,256x256
[16x16]
Size=16
[48x48]
Size=48
[256x256]
Size=256

View File

@@ -1,13 +1,6 @@
<RCC>
<qresource prefix="icons/qdarkstyle">
<file alias="index.theme">icons/index.theme</file>
<file alias="16x16/lock.png">icons/16x16/lock.png</file>
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
<file alias="48x48/chip.png">icons/48x48/chip.png</file>
<file alias="48x48/folder.png">icons/48x48/folder.png</file>
<file alias="48x48/plus.png">icons/48x48/plus.png</file>
<file alias="48x48/sd_card.png">icons/48x48/sd_card.png</file>
<file alias="256x256/plus_folder.png">icons/256x256/plus_folder.png</file>
</qresource>
<qresource prefix="qss_icons">
<file>rc/up_arrow_disabled.png</file>

View File

@@ -77,12 +77,6 @@ if (ENABLE_VULKAN)
add_subdirectory(sirit)
endif()
# zlib
add_subdirectory(zlib EXCLUDE_FROM_ALL)
# libzip
add_subdirectory(libzip EXCLUDE_FROM_ALL)
if (ENABLE_WEB_SERVICE)
# LibreSSL
set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")

2
externals/fmt vendored

1
externals/libzip vendored

Submodule externals/libzip deleted from bd7a8103e9

1
externals/zlib vendored

Submodule externals/zlib deleted from cacf7f1d4e

View File

@@ -337,28 +337,3 @@ proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
The icons used in this project have the following licenses:
Icon Name | License | Origin/Author
--- | --- | ---
checked.png | Free for non-commercial use
failed.png | Free for non-commercial use
lock.png | CC BY-ND 3.0 | https://icons8.com
plus_folder.png (Default, Dark) | CC BY-ND 3.0 | https://icons8.com
bad_folder.png (Default, Dark) | CC BY-ND 3.0 | https://icons8.com
chip.png (Default, Dark) | CC BY-ND 3.0 | https://icons8.com
folder.png (Default, Dark) | CC BY-ND 3.0 | https://icons8.com
plus.png (Default, Dark) | CC0 1.0 | Designed by BreadFish64 from the Citra team
sd_card.png (Default, Dark) | CC BY-ND 3.0 | https://icons8.com
plus_folder.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
bad_folder.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
chip.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
folder.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
plus.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
sd_card.png (Colorful, Colorful Dark) | CC BY-ND 3.0 | https://icons8.com
Note:
Some icons are different in different themes, and they are separately listed
only when they have different licenses/origins.

View File

@@ -107,11 +107,6 @@ Stream::State AudioRenderer::GetStreamState() const {
return stream->GetState();
}
static constexpr u32 VersionFromRevision(u32_le rev) {
// "REV7" -> 7
return ((rev >> 24) & 0xff) - 0x30;
}
std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) {
// Copy UpdateDataHeader struct
UpdateDataHeader config{};
@@ -171,11 +166,6 @@ std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_
// Copy output header
UpdateDataHeader response_data{worker_params};
std::vector<u8> output_params(response_data.total_size);
const auto audren_revision = VersionFromRevision(config.revision);
if (audren_revision >= 5) {
response_data.frame_count = 0x10;
response_data.total_size += 0x10;
}
std::memcpy(output_params.data(), &response_data, sizeof(UpdateDataHeader));
// Copy output memory pool entries

View File

@@ -194,24 +194,21 @@ struct UpdateDataHeader {
mixes_size = 0x0;
sinks_size = config.sink_count * 0x20;
performance_manager_size = 0x10;
frame_count = 0;
total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size + voices_size +
effects_size + sinks_size + performance_manager_size;
}
u32_le revision{};
u32_le behavior_size{};
u32_le memory_pools_size{};
u32_le voices_size{};
u32_le voice_resource_size{};
u32_le effects_size{};
u32_le mixes_size{};
u32_le sinks_size{};
u32_le performance_manager_size{};
INSERT_PADDING_WORDS(1);
u32_le frame_count{};
INSERT_PADDING_WORDS(4);
u32_le total_size{};
u32_le revision;
u32_le behavior_size;
u32_le memory_pools_size;
u32_le voices_size;
u32_le voice_resource_size;
u32_le effects_size;
u32_le mixes_size;
u32_le sinks_size;
u32_le performance_manager_size;
INSERT_PADDING_WORDS(6);
u32_le total_size;
};
static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size");

View File

@@ -10,28 +10,13 @@ if (DEFINED ENV{CI})
elseif(DEFINED ENV{APPVEYOR})
set(BUILD_REPOSITORY $ENV{APPVEYOR_REPO_NAME})
set(BUILD_TAG $ENV{APPVEYOR_REPO_TAG_NAME})
elseif(DEFINED ENV{AZURE})
set(BUILD_REPOSITORY $ENV{AZURE_REPO_NAME})
set(BUILD_TAG $ENV{AZURE_REPO_TAG})
endif()
endif()
if (DEFINED ENV{TITLEBARFORMATIDLE})
set(TITLE_BAR_FORMAT_IDLE $ENV{TITLEBARFORMATIDLE})
endif ()
if (DEFINED ENV{TITLEBARFORMATRUNNING})
set(TITLE_BAR_FORMAT_RUNNING $ENV{TITLEBARFORMATRUNNING})
endif ()
if (DEFINED ENV{DISPLAYVERSION})
set(DISPLAY_VERSION $ENV{DISPLAYVERSION})
endif ()
add_custom_command(OUTPUT scm_rev.cpp
COMMAND ${CMAKE_COMMAND}
-DSRC_DIR="${CMAKE_SOURCE_DIR}"
-DBUILD_REPOSITORY="${BUILD_REPOSITORY}"
-DTITLE_BAR_FORMAT_IDLE="${TITLE_BAR_FORMAT_IDLE}"
-DTITLE_BAR_FORMAT_RUNNING="${TITLE_BAR_FORMAT_RUNNING}"
-DBUILD_TAG="${BUILD_TAG}"
-DBUILD_ID="${DISPLAY_VERSION}"
-P "${CMAKE_SOURCE_DIR}/CMakeModules/GenerateSCMRev.cmake"
DEPENDS
# WARNING! It was too much work to try and make a common location for this list,

View File

@@ -255,7 +255,6 @@ void DebuggerBackend::Write(const Entry& entry) {
CLS(Input) \
CLS(Network) \
CLS(Loader) \
CLS(CheatEngine) \
CLS(Crypto) \
CLS(WebService)

View File

@@ -117,7 +117,6 @@ enum class Class : ClassType {
Audio_DSP, ///< The HLE implementation of the DSP
Audio_Sink, ///< Emulator audio output backend
Loader, ///< ROM loader
CheatEngine, ///< Memory manipulation and engine VM functions
Crypto, ///< Cryptographic engine/functions
Input, ///< Input emulation
Network, ///< Network emulation

View File

@@ -11,9 +11,6 @@
#define BUILD_DATE "@BUILD_DATE@"
#define BUILD_FULLNAME "@BUILD_FULLNAME@"
#define BUILD_VERSION "@BUILD_VERSION@"
#define BUILD_ID "@BUILD_ID@"
#define TITLE_BAR_FORMAT_IDLE "@TITLE_BAR_FORMAT_IDLE@"
#define TITLE_BAR_FORMAT_RUNNING "@TITLE_BAR_FORMAT_RUNNING@"
#define SHADER_CACHE_VERSION "@SHADER_CACHE_VERSION@"
namespace Common {
@@ -25,9 +22,6 @@ const char g_build_name[] = BUILD_NAME;
const char g_build_date[] = BUILD_DATE;
const char g_build_fullname[] = BUILD_FULLNAME;
const char g_build_version[] = BUILD_VERSION;
const char g_build_id[] = BUILD_ID;
const char g_title_bar_format_idle[] = TITLE_BAR_FORMAT_IDLE;
const char g_title_bar_format_running[] = TITLE_BAR_FORMAT_RUNNING;
const char g_shader_cache_version[] = SHADER_CACHE_VERSION;
} // namespace

View File

@@ -13,9 +13,6 @@ extern const char g_build_name[];
extern const char g_build_date[];
extern const char g_build_fullname[];
extern const char g_build_version[];
extern const char g_build_id[];
extern const char g_title_bar_format_idle[];
extern const char g_title_bar_format_running[];
extern const char g_shader_cache_version[];
} // namespace Common

View File

@@ -1,9 +1,3 @@
if (YUZU_ENABLE_BOXCAT)
set(BCAT_BOXCAT_ADDITIONAL_SOURCES hle/service/bcat/backend/boxcat.cpp hle/service/bcat/backend/boxcat.h)
else()
set(BCAT_BOXCAT_ADDITIONAL_SOURCES)
endif()
add_library(core STATIC
arm/arm_interface.h
arm/arm_interface.cpp
@@ -39,6 +33,8 @@ add_library(core STATIC
file_sys/bis_factory.h
file_sys/card_image.cpp
file_sys/card_image.h
file_sys/cheat_engine.cpp
file_sys/cheat_engine.h
file_sys/content_archive.cpp
file_sys/content_archive.h
file_sys/control_metadata.cpp
@@ -74,8 +70,6 @@ add_library(core STATIC
file_sys/sdmc_factory.h
file_sys/submission_package.cpp
file_sys/submission_package.h
file_sys/system_archive/mii_model.cpp
file_sys/system_archive/mii_model.h
file_sys/system_archive/ng_word.cpp
file_sys/system_archive/ng_word.h
file_sys/system_archive/system_archive.cpp
@@ -88,8 +82,6 @@ add_library(core STATIC
file_sys/vfs_concat.h
file_sys/vfs_layered.cpp
file_sys/vfs_layered.h
file_sys/vfs_libzip.cpp
file_sys/vfs_libzip.h
file_sys/vfs_offset.cpp
file_sys/vfs_offset.h
file_sys/vfs_real.cpp
@@ -249,9 +241,6 @@ add_library(core STATIC
hle/service/audio/errors.h
hle/service/audio/hwopus.cpp
hle/service/audio/hwopus.h
hle/service/bcat/backend/backend.cpp
hle/service/bcat/backend/backend.h
${BCAT_BOXCAT_ADDITIONAL_SOURCES}
hle/service/bcat/bcat.cpp
hle/service/bcat/bcat.h
hle/service/bcat/module.cpp
@@ -486,11 +475,6 @@ add_library(core STATIC
loader/nsp.h
loader/xci.cpp
loader/xci.h
memory/cheat_engine.cpp
memory/cheat_engine.h
memory/dmnt_cheat_types.h
memory/dmnt_cheat_vm.cpp
memory/dmnt_cheat_vm.h
memory.cpp
memory.h
memory_setup.h
@@ -510,15 +494,6 @@ create_target_directory_groups(core)
target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt json-headers mbedtls opus unicorn open_source_archives)
if (YUZU_ENABLE_BOXCAT)
get_directory_property(OPENSSL_LIBS
DIRECTORY ${PROJECT_SOURCE_DIR}/externals/libressl
DEFINITION OPENSSL_LIBS)
target_compile_definitions(core PRIVATE -DCPPHTTPLIB_OPENSSL_SUPPORT -DYUZU_ENABLE_BOXCAT)
target_link_libraries(core PRIVATE httplib json-headers ${OPENSSL_LIBS} zip)
endif()
if (ENABLE_WEB_SERVICE)
target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE)
target_link_libraries(core PRIVATE web_service)

View File

@@ -14,14 +14,8 @@
#include "core/core_cpu.h"
#include "core/core_timing.h"
#include "core/cpu_core_manager.h"
#include "core/file_sys/bis_factory.h"
#include "core/file_sys/card_image.h"
#include "core/file_sys/mode.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs_factory.h"
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/sdmc_factory.h"
#include "core/file_sys/vfs_concat.h"
#include "core/file_sys/vfs_real.h"
#include "core/gdbstub/gdbstub.h"
@@ -33,17 +27,17 @@
#include "core/hle/kernel/thread.h"
#include "core/hle/service/am/applets/applets.h"
#include "core/hle/service/apm/controller.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/glue/manager.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
#include "core/loader/loader.h"
#include "core/memory/cheat_engine.h"
#include "core/perf_stats.h"
#include "core/reporter.h"
#include "core/settings.h"
#include "core/telemetry_session.h"
#include "core/tools/freezer.h"
#include "file_sys/cheat_engine.h"
#include "file_sys/patch_manager.h"
#include "video_core/debug_utils/debug_utils.h"
#include "video_core/renderer_base.h"
#include "video_core/video_core.h"
@@ -110,8 +104,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
return vfs->OpenFile(path, FileSys::Mode::Read);
}
struct System::Impl {
explicit Impl(System& system)
: kernel{system}, cpu_core_manager{system}, applet_manager{system}, reporter{system} {}
explicit Impl(System& system) : kernel{system}, cpu_core_manager{system}, reporter{system} {}
Cpu& CurrentCpuCore() {
return cpu_core_manager.GetCurrentCore();
@@ -163,10 +156,13 @@ struct System::Impl {
gpu_core = VideoCore::CreateGPU(system);
is_powered_on = true;
exit_lock = false;
LOG_DEBUG(Core, "Initialized OK");
// Reset counters and set time origin to current frame
GetAndResetPerfStats();
perf_stats.BeginSystemFrame();
return ResultStatus::Success;
}
@@ -205,34 +201,10 @@ struct System::Impl {
gpu_core->Start();
cpu_core_manager.StartThreads();
// Initialize cheat engine
if (cheat_engine) {
cheat_engine->Initialize();
}
// All threads are started, begin main process execution, now that we're in the clear.
main_process->Run(load_parameters->main_thread_priority,
load_parameters->main_thread_stack_size);
if (Settings::values.gamecard_inserted) {
if (Settings::values.gamecard_current_game) {
fs_controller.SetGameCard(GetGameFileFromPath(virtual_filesystem, filepath));
} else if (!Settings::values.gamecard_path.empty()) {
fs_controller.SetGameCard(
GetGameFileFromPath(virtual_filesystem, Settings::values.gamecard_path));
}
}
u64 title_id{0};
if (app_loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) {
LOG_ERROR(Core, "Failed to find title id for ROM (Error {})",
static_cast<u32>(load_result));
}
perf_stats = std::make_unique<PerfStats>(title_id);
// Reset counters and set time origin to current frame
GetAndResetPerfStats();
perf_stats->BeginSystemFrame();
status = ResultStatus::Success;
return status;
}
@@ -246,11 +218,8 @@ struct System::Impl {
perf_results.game_fps);
telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime",
perf_results.frametime * 1000.0);
telemetry_session->AddField(Telemetry::FieldType::Performance, "Mean_Frametime_MS",
perf_stats->GetMeanFrametime());
is_powered_on = false;
exit_lock = false;
// Shutdown emulation session
renderer.reset();
@@ -259,7 +228,6 @@ struct System::Impl {
service_manager.reset();
cheat_engine.reset();
telemetry_session.reset();
perf_stats.reset();
gpu_core.reset();
// Close all CPU/threading state
@@ -317,7 +285,7 @@ struct System::Impl {
}
PerfStatsResults GetAndResetPerfStats() {
return perf_stats->GetAndResetStats(core_timing.GetGlobalTimeUs());
return perf_stats.GetAndResetStats(core_timing.GetGlobalTimeUs());
}
Timing::CoreTiming core_timing;
@@ -326,7 +294,6 @@ struct System::Impl {
FileSys::VirtualFilesystem virtual_filesystem;
/// ContentProviderUnion instance
std::unique_ptr<FileSys::ContentProviderUnion> content_provider;
Service::FileSystem::FileSystemController fs_controller;
/// AppLoader used to load the current executing application
std::unique_ptr<Loader::AppLoader> app_loader;
std::unique_ptr<VideoCore::RendererBase> renderer;
@@ -335,11 +302,9 @@ struct System::Impl {
std::unique_ptr<Core::Hardware::InterruptManager> interrupt_manager;
CpuCoreManager cpu_core_manager;
bool is_powered_on = false;
bool exit_lock = false;
std::unique_ptr<Memory::CheatEngine> cheat_engine;
std::unique_ptr<FileSys::CheatEngine> cheat_engine;
std::unique_ptr<Tools::Freezer> memory_freezer;
std::array<u8, 0x20> build_id{};
/// Frontend applets
Service::AM::Applets::AppletManager applet_manager;
@@ -361,7 +326,7 @@ struct System::Impl {
ResultStatus status = ResultStatus::Success;
std::string status_details = "";
std::unique_ptr<Core::PerfStats> perf_stats;
Core::PerfStats perf_stats;
Core::FrameLimiter frame_limiter;
};
@@ -514,11 +479,11 @@ const Timing::CoreTiming& System::CoreTiming() const {
}
Core::PerfStats& System::GetPerfStats() {
return *impl->perf_stats;
return impl->perf_stats;
}
const Core::PerfStats& System::GetPerfStats() const {
return *impl->perf_stats;
return impl->perf_stats;
}
Core::FrameLimiter& System::FrameLimiter() {
@@ -553,6 +518,13 @@ Tegra::DebugContext* System::GetGPUDebugContext() const {
return impl->debug_context.get();
}
void System::RegisterCheatList(const std::vector<FileSys::CheatList>& list,
const std::string& build_id, VAddr code_region_start,
VAddr code_region_end) {
impl->cheat_engine = std::make_unique<FileSys::CheatEngine>(*this, list, build_id,
code_region_start, code_region_end);
}
void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) {
impl->virtual_filesystem = std::move(vfs);
}
@@ -561,13 +533,6 @@ std::shared_ptr<FileSys::VfsFilesystem> System::GetFilesystem() const {
return impl->virtual_filesystem;
}
void System::RegisterCheatList(const std::vector<Memory::CheatEntry>& list,
const std::array<u8, 32>& build_id, VAddr main_region_begin,
u64 main_region_size) {
impl->cheat_engine = std::make_unique<Memory::CheatEngine>(*this, list, build_id);
impl->cheat_engine->SetMainMemoryParameters(main_region_begin, main_region_size);
}
void System::SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set) {
impl->applet_manager.SetAppletFrontendSet(std::move(set));
}
@@ -596,14 +561,6 @@ const FileSys::ContentProvider& System::GetContentProvider() const {
return *impl->content_provider;
}
Service::FileSystem::FileSystemController& System::GetFileSystemController() {
return impl->fs_controller;
}
const Service::FileSystem::FileSystemController& System::GetFileSystemController() const {
return impl->fs_controller;
}
void System::RegisterContentProvider(FileSys::ContentProviderUnionSlot slot,
FileSys::ContentProvider* provider) {
impl->content_provider->SetSlot(slot, provider);
@@ -633,22 +590,6 @@ const Service::APM::Controller& System::GetAPMController() const {
return impl->apm_controller;
}
void System::SetExitLock(bool locked) {
impl->exit_lock = locked;
}
bool System::GetExitLock() const {
return impl->exit_lock;
}
void System::SetCurrentProcessBuildID(std::array<u8, 32> id) {
impl->build_id = id;
}
const std::array<u8, 32>& System::GetCurrentProcessBuildID() const {
return impl->build_id;
}
System::ResultStatus System::Init(Frontend::EmuWindow& emu_window) {
return impl->Init(*this, emu_window);
}

View File

@@ -18,6 +18,7 @@ class EmuWindow;
} // namespace Core::Frontend
namespace FileSys {
class CheatList;
class ContentProvider;
class ContentProviderUnion;
enum class ContentProviderUnionSlot;
@@ -35,10 +36,6 @@ class AppLoader;
enum class ResultStatus : u16;
} // namespace Loader
namespace Memory {
struct CheatEntry;
} // namespace Memory
namespace Service {
namespace AM::Applets {
@@ -50,10 +47,6 @@ namespace APM {
class Controller;
}
namespace FileSystem {
class FileSystemController;
} // namespace FileSystem
namespace Glue {
class ARPManager;
}
@@ -289,9 +282,8 @@ public:
std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const;
void RegisterCheatList(const std::vector<Memory::CheatEntry>& list,
const std::array<u8, 0x20>& build_id, VAddr main_region_begin,
u64 main_region_size);
void RegisterCheatList(const std::vector<FileSys::CheatList>& list, const std::string& build_id,
VAddr code_region_start, VAddr code_region_end);
void SetAppletFrontendSet(Service::AM::Applets::AppletFrontendSet&& set);
@@ -307,10 +299,6 @@ public:
const FileSys::ContentProvider& GetContentProvider() const;
Service::FileSystem::FileSystemController& GetFileSystemController();
const Service::FileSystem::FileSystemController& GetFileSystemController() const;
void RegisterContentProvider(FileSys::ContentProviderUnionSlot slot,
FileSys::ContentProvider* provider);
@@ -326,14 +314,6 @@ public:
const Service::APM::Controller& GetAPMController() const;
void SetExitLock(bool locked);
bool GetExitLock() const;
void SetCurrentProcessBuildID(std::array<u8, 0x20> id);
const std::array<u8, 0x20>& GetCurrentProcessBuildID() const;
private:
System();

View File

@@ -37,7 +37,6 @@
namespace Core::Crypto {
constexpr u64 CURRENT_CRYPTO_REVISION = 0x5;
constexpr u64 FULL_TICKET_SIZE = 0x400;
using namespace Common;
@@ -56,99 +55,6 @@ const std::map<std::pair<S128KeyType, u64>, std::string> KEYS_VARIABLE_LENGTH{
{{S128KeyType::KeyblobMAC, 0}, "keyblob_mac_key_"},
};
namespace {
template <std::size_t Size>
bool IsAllZeroArray(const std::array<u8, Size>& array) {
return std::all_of(array.begin(), array.end(), [](const auto& elem) { return elem == 0; });
}
} // namespace
u64 GetSignatureTypeDataSize(SignatureType type) {
switch (type) {
case SignatureType::RSA_4096_SHA1:
case SignatureType::RSA_4096_SHA256:
return 0x200;
case SignatureType::RSA_2048_SHA1:
case SignatureType::RSA_2048_SHA256:
return 0x100;
case SignatureType::ECDSA_SHA1:
case SignatureType::ECDSA_SHA256:
return 0x3C;
}
UNREACHABLE();
}
u64 GetSignatureTypePaddingSize(SignatureType type) {
switch (type) {
case SignatureType::RSA_4096_SHA1:
case SignatureType::RSA_4096_SHA256:
case SignatureType::RSA_2048_SHA1:
case SignatureType::RSA_2048_SHA256:
return 0x3C;
case SignatureType::ECDSA_SHA1:
case SignatureType::ECDSA_SHA256:
return 0x40;
}
UNREACHABLE();
}
SignatureType Ticket::GetSignatureType() const {
if (auto ticket = std::get_if<RSA4096Ticket>(&data)) {
return ticket->sig_type;
}
if (auto ticket = std::get_if<RSA2048Ticket>(&data)) {
return ticket->sig_type;
}
if (auto ticket = std::get_if<ECDSATicket>(&data)) {
return ticket->sig_type;
}
UNREACHABLE();
}
TicketData& Ticket::GetData() {
if (auto ticket = std::get_if<RSA4096Ticket>(&data)) {
return ticket->data;
}
if (auto ticket = std::get_if<RSA2048Ticket>(&data)) {
return ticket->data;
}
if (auto ticket = std::get_if<ECDSATicket>(&data)) {
return ticket->data;
}
UNREACHABLE();
}
const TicketData& Ticket::GetData() const {
if (auto ticket = std::get_if<RSA4096Ticket>(&data)) {
return ticket->data;
}
if (auto ticket = std::get_if<RSA2048Ticket>(&data)) {
return ticket->data;
}
if (auto ticket = std::get_if<ECDSATicket>(&data)) {
return ticket->data;
}
UNREACHABLE();
}
u64 Ticket::GetSize() const {
const auto sig_type = GetSignatureType();
return sizeof(SignatureType) + GetSignatureTypeDataSize(sig_type) +
GetSignatureTypePaddingSize(sig_type) + sizeof(TicketData);
}
Ticket Ticket::SynthesizeCommon(Key128 title_key, const std::array<u8, 16>& rights_id) {
RSA2048Ticket out{};
out.sig_type = SignatureType::RSA_2048_SHA256;
out.data.rights_id = rights_id;
out.data.title_key_common = title_key;
return Ticket{out};
}
Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed) {
Key128 out{};
@@ -229,27 +135,6 @@ void KeyManager::DeriveGeneralPurposeKeys(std::size_t crypto_revision) {
}
}
RSAKeyPair<2048> KeyManager::GetETicketRSAKey() const {
if (IsAllZeroArray(eticket_extended_kek) || !HasKey(S128KeyType::ETicketRSAKek))
return {};
const auto eticket_final = GetKey(S128KeyType::ETicketRSAKek);
std::vector<u8> extended_iv(eticket_extended_kek.begin(), eticket_extended_kek.begin() + 0x10);
std::array<u8, 0x230> extended_dec{};
AESCipher<Key128> rsa_1(eticket_final, Mode::CTR);
rsa_1.SetIV(extended_iv);
rsa_1.Transcode(eticket_extended_kek.data() + 0x10, eticket_extended_kek.size() - 0x10,
extended_dec.data(), Op::Decrypt);
RSAKeyPair<2048> rsa_key{};
std::memcpy(rsa_key.decryption_key.data(), extended_dec.data(), rsa_key.decryption_key.size());
std::memcpy(rsa_key.modulus.data(), extended_dec.data() + 0x100, rsa_key.modulus.size());
std::memcpy(rsa_key.exponent.data(), extended_dec.data() + 0x200, rsa_key.exponent.size());
return rsa_key;
}
Key128 DeriveKeyblobMACKey(const Key128& keyblob_key, const Key128& mac_source) {
AESCipher<Key128> mac_cipher(keyblob_key, Mode::ECB);
Key128 mac_key{};
@@ -352,7 +237,7 @@ Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& ke
return Loader::ResultStatus::Success;
}
std::vector<Ticket> GetTicketblob(const FileUtil::IOFile& ticket_save) {
std::vector<TicketRaw> GetTicketblob(const FileUtil::IOFile& ticket_save) {
if (!ticket_save.IsOpen())
return {};
@@ -361,14 +246,14 @@ std::vector<Ticket> GetTicketblob(const FileUtil::IOFile& ticket_save) {
return {};
}
std::vector<Ticket> out;
std::vector<TicketRaw> out;
for (std::size_t offset = 0; offset + 0x4 < buffer.size(); ++offset) {
if (buffer[offset] == 0x4 && buffer[offset + 1] == 0x0 && buffer[offset + 2] == 0x1 &&
buffer[offset + 3] == 0x0) {
out.emplace_back();
auto& next = out.back();
std::memcpy(&next, buffer.data() + offset, sizeof(Ticket));
offset += FULL_TICKET_SIZE;
std::memcpy(&next, buffer.data() + offset, sizeof(TicketRaw));
offset += next.size();
}
}
@@ -420,23 +305,29 @@ static std::optional<u64> FindTicketOffset(const std::array<u8, size>& data) {
return offset;
}
std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket,
std::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket,
const RSAKeyPair<2048>& key) {
const auto issuer = ticket.GetData().issuer;
if (IsAllZeroArray(issuer))
u32 cert_authority;
std::memcpy(&cert_authority, ticket.data() + 0x140, sizeof(cert_authority));
if (cert_authority == 0)
return {};
if (issuer[0] != 'R' || issuer[1] != 'o' || issuer[2] != 'o' || issuer[3] != 't') {
LOG_INFO(Crypto, "Attempting to parse ticket with non-standard certificate authority.");
if (cert_authority != Common::MakeMagic('R', 'o', 'o', 't')) {
LOG_INFO(Crypto,
"Attempting to parse ticket with non-standard certificate authority {:08X}.",
cert_authority);
}
Key128 rights_id = ticket.GetData().rights_id;
Key128 rights_id;
std::memcpy(rights_id.data(), ticket.data() + 0x2A0, sizeof(Key128));
if (rights_id == Key128{})
return {};
if (!std::any_of(ticket.GetData().title_key_common_pad.begin(),
ticket.GetData().title_key_common_pad.end(), [](u8 b) { return b != 0; })) {
return std::make_pair(rights_id, ticket.GetData().title_key_common);
Key128 key_temp{};
if (!std::any_of(ticket.begin() + 0x190, ticket.begin() + 0x280, [](u8 b) { return b != 0; })) {
std::memcpy(key_temp.data(), ticket.data() + 0x180, key_temp.size());
return std::make_pair(rights_id, key_temp);
}
mbedtls_mpi D; // RSA Private Exponent
@@ -451,7 +342,7 @@ std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket,
mbedtls_mpi_read_binary(&D, key.decryption_key.data(), key.decryption_key.size());
mbedtls_mpi_read_binary(&N, key.modulus.data(), key.modulus.size());
mbedtls_mpi_read_binary(&S, ticket.GetData().title_key_block.data(), 0x100);
mbedtls_mpi_read_binary(&S, ticket.data() + 0x180, 0x100);
mbedtls_mpi_exp_mod(&M, &S, &D, &N, nullptr);
@@ -475,7 +366,6 @@ std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket,
return {};
ASSERT(*offset > 0);
Key128 key_temp{};
std::memcpy(key_temp.data(), m_2.data() + *offset, key_temp.size());
return std::make_pair(rights_id, key_temp);
@@ -560,8 +450,6 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) {
const auto index = std::stoul(out[0].substr(18, 2), nullptr, 16);
encrypted_keyblobs[index] = Common::HexStringToArray<0xB0>(out[1]);
} else if (out[0].compare(0, 20, "eticket_extended_kek") == 0) {
eticket_extended_kek = Common::HexStringToArray<576>(out[1]);
} else {
for (const auto& kv : KEYS_VARIABLE_LENGTH) {
if (!ValidCryptoRevisionString(out[0], kv.second.size(), 2))
@@ -974,19 +862,20 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) {
// Titlekeys
data.DecryptProdInfo(GetBISKey(0));
eticket_extended_kek = data.GetETicketExtendedKek();
WriteKeyToFile(KeyCategory::Console, "eticket_extended_kek", eticket_extended_kek);
PopulateTickets();
}
const auto eticket_extended_kek = data.GetETicketExtendedKek();
void KeyManager::PopulateTickets() {
const auto rsa_key = GetETicketRSAKey();
std::vector<u8> extended_iv(0x10);
std::memcpy(extended_iv.data(), eticket_extended_kek.data(), extended_iv.size());
std::array<u8, 0x230> extended_dec{};
AESCipher<Key128> rsa_1(eticket_final, Mode::CTR);
rsa_1.SetIV(extended_iv);
rsa_1.Transcode(eticket_extended_kek.data() + 0x10, eticket_extended_kek.size() - 0x10,
extended_dec.data(), Op::Decrypt);
if (rsa_key == RSAKeyPair<2048>{})
return;
if (!common_tickets.empty() && !personal_tickets.empty())
return;
RSAKeyPair<2048> rsa_key{};
std::memcpy(rsa_key.decryption_key.data(), extended_dec.data(), rsa_key.decryption_key.size());
std::memcpy(rsa_key.modulus.data(), extended_dec.data() + 0x100, rsa_key.modulus.size());
std::memcpy(rsa_key.exponent.data(), extended_dec.data() + 0x200, rsa_key.exponent.size());
const FileUtil::IOFile save1(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
"/system/save/80000000000000e1",
@@ -997,41 +886,19 @@ void KeyManager::PopulateTickets() {
const auto blob2 = GetTicketblob(save2);
auto res = GetTicketblob(save1);
const auto idx = res.size();
res.insert(res.end(), blob2.begin(), blob2.end());
for (std::size_t i = 0; i < res.size(); ++i) {
const auto common = i < idx;
const auto pair = ParseTicket(res[i], rsa_key);
for (const auto& raw : res) {
const auto pair = ParseTicket(raw, rsa_key);
if (!pair)
continue;
const auto& [rid, key] = *pair;
u128 rights_id;
std::memcpy(rights_id.data(), rid.data(), rid.size());
if (common) {
common_tickets[rights_id] = res[i];
} else {
personal_tickets[rights_id] = res[i];
}
SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
}
}
void KeyManager::SynthesizeTickets() {
for (const auto& key : s128_keys) {
if (key.first.type != S128KeyType::Titlekey) {
continue;
}
u128 rights_id{key.first.field1, key.first.field2};
Key128 rights_id_2;
std::memcpy(rights_id_2.data(), rights_id.data(), rights_id_2.size());
const auto ticket = Ticket::SynthesizeCommon(key.second, rights_id_2);
common_tickets.insert_or_assign(rights_id, ticket);
}
}
void KeyManager::SetKeyWrapped(S128KeyType id, Key128 key, u64 field1, u64 field2) {
if (key == Key128{})
return;
@@ -1130,46 +997,6 @@ void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) {
DeriveBase();
}
const std::map<u128, Ticket>& KeyManager::GetCommonTickets() const {
return common_tickets;
}
const std::map<u128, Ticket>& KeyManager::GetPersonalizedTickets() const {
return personal_tickets;
}
bool KeyManager::AddTicketCommon(Ticket raw) {
const auto rsa_key = GetETicketRSAKey();
if (rsa_key == RSAKeyPair<2048>{})
return false;
const auto pair = ParseTicket(raw, rsa_key);
if (!pair)
return false;
const auto& [rid, key] = *pair;
u128 rights_id;
std::memcpy(rights_id.data(), rid.data(), rid.size());
common_tickets[rights_id] = raw;
SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
return true;
}
bool KeyManager::AddTicketPersonalized(Ticket raw) {
const auto rsa_key = GetETicketRSAKey();
if (rsa_key == RSAKeyPair<2048>{})
return false;
const auto pair = ParseTicket(raw, rsa_key);
if (!pair)
return false;
const auto& [rid, key] = *pair;
u128 rights_id;
std::memcpy(rights_id.data(), rid.data(), rid.size());
common_tickets[rights_id] = raw;
SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
return true;
}
const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> KeyManager::s128_file_id = {
{"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}},
{"eticket_rsa_kek_source",

View File

@@ -9,10 +9,8 @@
#include <optional>
#include <string>
#include <variant>
#include <boost/container/flat_map.hpp>
#include <fmt/format.h>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "core/crypto/partition_data_manager.h"
#include "core/file_sys/vfs_types.h"
@@ -32,79 +30,7 @@ constexpr u64 TICKET_FILE_TITLEKEY_OFFSET = 0x180;
using Key128 = std::array<u8, 0x10>;
using Key256 = std::array<u8, 0x20>;
using SHA256Hash = std::array<u8, 0x20>;
enum class SignatureType {
RSA_4096_SHA1 = 0x10000,
RSA_2048_SHA1 = 0x10001,
ECDSA_SHA1 = 0x10002,
RSA_4096_SHA256 = 0x10003,
RSA_2048_SHA256 = 0x10004,
ECDSA_SHA256 = 0x10005,
};
u64 GetSignatureTypeDataSize(SignatureType type);
u64 GetSignatureTypePaddingSize(SignatureType type);
enum class TitleKeyType : u8 {
Common = 0,
Personalized = 1,
};
struct TicketData {
std::array<u8, 0x40> issuer;
union {
std::array<u8, 0x100> title_key_block;
struct {
Key128 title_key_common;
std::array<u8, 0xF0> title_key_common_pad;
};
};
INSERT_PADDING_BYTES(0x1);
TitleKeyType type;
INSERT_PADDING_BYTES(0x3);
u8 revision;
INSERT_PADDING_BYTES(0xA);
u64 ticket_id;
u64 device_id;
std::array<u8, 0x10> rights_id;
u32 account_id;
INSERT_PADDING_BYTES(0x14C);
};
static_assert(sizeof(TicketData) == 0x2C0, "TicketData has incorrect size.");
struct RSA4096Ticket {
SignatureType sig_type;
std::array<u8, 0x200> sig_data;
INSERT_PADDING_BYTES(0x3C);
TicketData data;
};
struct RSA2048Ticket {
SignatureType sig_type;
std::array<u8, 0x100> sig_data;
INSERT_PADDING_BYTES(0x3C);
TicketData data;
};
struct ECDSATicket {
SignatureType sig_type;
std::array<u8, 0x3C> sig_data;
INSERT_PADDING_BYTES(0x40);
TicketData data;
};
struct Ticket {
std::variant<RSA4096Ticket, RSA2048Ticket, ECDSATicket> data;
SignatureType GetSignatureType() const;
TicketData& GetData();
const TicketData& GetData() const;
u64 GetSize() const;
static Ticket SynthesizeCommon(Key128 title_key, const std::array<u8, 0x10>& rights_id);
};
using TicketRaw = std::array<u8, 0x400>;
static_assert(sizeof(Key128) == 16, "Key128 must be 128 bytes big.");
static_assert(sizeof(Key256) == 32, "Key256 must be 256 bytes big.");
@@ -117,19 +43,6 @@ struct RSAKeyPair {
std::array<u8, 4> exponent;
};
template <size_t bit_size, size_t byte_size>
bool operator==(const RSAKeyPair<bit_size, byte_size>& lhs,
const RSAKeyPair<bit_size, byte_size>& rhs) {
return std::tie(lhs.encryption_key, lhs.decryption_key, lhs.modulus, lhs.exponent) ==
std::tie(rhs.encryption_key, rhs.decryption_key, rhs.modulus, rhs.exponent);
}
template <size_t bit_size, size_t byte_size>
bool operator!=(const RSAKeyPair<bit_size, byte_size>& lhs,
const RSAKeyPair<bit_size, byte_size>& rhs) {
return !(lhs == rhs);
}
enum class KeyCategory : u8 {
Standard,
Title,
@@ -238,35 +151,22 @@ public:
static bool KeyFileExists(bool title);
// Call before using the sd seed to attempt to derive it if it dosen't exist. Needs system
// save 8*43 and the private file to exist.
// Call before using the sd seed to attempt to derive it if it dosen't exist. Needs system save
// 8*43 and the private file to exist.
void DeriveSDSeedLazy();
bool BaseDeriveNecessary() const;
void DeriveBase();
void DeriveETicket(PartitionDataManager& data);
void PopulateTickets();
void SynthesizeTickets();
void PopulateFromPartitionData(PartitionDataManager& data);
const std::map<u128, Ticket>& GetCommonTickets() const;
const std::map<u128, Ticket>& GetPersonalizedTickets() const;
bool AddTicketCommon(Ticket raw);
bool AddTicketPersonalized(Ticket raw);
private:
std::map<KeyIndex<S128KeyType>, Key128> s128_keys;
std::map<KeyIndex<S256KeyType>, Key256> s256_keys;
// Map from rights ID to ticket
std::map<u128, Ticket> common_tickets;
std::map<u128, Ticket> personal_tickets;
std::array<std::array<u8, 0xB0>, 0x20> encrypted_keyblobs{};
std::array<std::array<u8, 0x90>, 0x20> keyblobs{};
std::array<u8, 576> eticket_extended_kek{};
bool dev_mode;
void LoadFromFile(const std::string& filename, bool is_title_keys);
@@ -278,8 +178,6 @@ private:
void DeriveGeneralPurposeKeys(std::size_t crypto_revision);
RSAKeyPair<2048> GetETicketRSAKey() const;
void SetKeyWrapped(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0);
void SetKeyWrapped(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0);
@@ -297,11 +195,11 @@ std::array<u8, 0x90> DecryptKeyblob(const std::array<u8, 0xB0>& encrypted_keyblo
std::optional<Key128> DeriveSDSeed();
Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys);
std::vector<Ticket> GetTicketblob(const FileUtil::IOFile& ticket_save);
std::vector<TicketRaw> GetTicketblob(const FileUtil::IOFile& ticket_save);
// Returns a pair of {rights_id, titlekey}. Fails if the ticket has no certificate authority
// (offset 0x140-0x144 is zero)
std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket,
// Returns a pair of {rights_id, titlekey}. Fails if the ticket has no certificate authority (offset
// 0x140-0x144 is zero)
std::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket,
const RSAKeyPair<2048>& eticket_extended_key);
} // namespace Core::Crypto

View File

@@ -480,10 +480,6 @@ void PartitionDataManager::DecryptProdInfo(std::array<u8, 0x20> bis_key) {
prodinfo_decrypted = std::make_shared<XTSEncryptionLayer>(prodinfo, bis_key);
}
FileSys::VirtualFile PartitionDataManager::GetDecryptedProdInfo() const {
return prodinfo_decrypted;
}
std::array<u8, 576> PartitionDataManager::GetETicketExtendedKek() const {
std::array<u8, 0x240> out{};
if (prodinfo_decrypted != nullptr)

View File

@@ -84,7 +84,6 @@ public:
bool HasProdInfo() const;
FileSys::VirtualFile GetProdInfoRaw() const;
void DecryptProdInfo(std::array<u8, 0x20> bis_key);
FileSys::VirtualFile GetDecryptedProdInfo() const;
std::array<u8, 0x240> GetETicketExtendedKek() const;
private:

View File

@@ -3,12 +3,8 @@
// Refer to the license.txt file included.
#include <fmt/format.h>
#include "common/file_util.h"
#include "core/core.h"
#include "core/file_sys/bis_factory.h"
#include "core/file_sys/mode.h"
#include "core/file_sys/registered_cache.h"
#include "core/settings.h"
namespace FileSys {
@@ -18,22 +14,10 @@ BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_, VirtualDir
sysnand_cache(std::make_unique<RegisteredCache>(
GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))),
usrnand_cache(std::make_unique<RegisteredCache>(
GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))),
sysnand_placeholder(std::make_unique<PlaceholderCache>(
GetOrCreateDirectoryRelative(nand_root, "/system/Contents/placehld"))),
usrnand_placeholder(std::make_unique<PlaceholderCache>(
GetOrCreateDirectoryRelative(nand_root, "/user/Contents/placehld"))) {}
GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))) {}
BISFactory::~BISFactory() = default;
VirtualDir BISFactory::GetSystemNANDContentDirectory() const {
return GetOrCreateDirectoryRelative(nand_root, "/system/Contents");
}
VirtualDir BISFactory::GetUserNANDContentDirectory() const {
return GetOrCreateDirectoryRelative(nand_root, "/user/Contents");
}
RegisteredCache* BISFactory::GetSystemNANDContents() const {
return sysnand_cache.get();
}
@@ -42,17 +26,9 @@ RegisteredCache* BISFactory::GetUserNANDContents() const {
return usrnand_cache.get();
}
PlaceholderCache* BISFactory::GetSystemNANDPlaceholder() const {
return sysnand_placeholder.get();
}
PlaceholderCache* BISFactory::GetUserNANDPlaceholder() const {
return usrnand_placeholder.get();
}
VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const {
// LayeredFS doesn't work on updates and title id-less homebrew
if (title_id == 0 || (title_id & 0xFFF) == 0x800)
if (title_id == 0 || (title_id & 0x800) > 0)
return nullptr;
return GetOrCreateDirectoryRelative(load_root, fmt::format("/{:016X}", title_id));
}
@@ -63,82 +39,4 @@ VirtualDir BISFactory::GetModificationDumpRoot(u64 title_id) const {
return GetOrCreateDirectoryRelative(dump_root, fmt::format("/{:016X}", title_id));
}
VirtualDir BISFactory::OpenPartition(BisPartitionId id) const {
switch (id) {
case BisPartitionId::CalibrationFile:
return GetOrCreateDirectoryRelative(nand_root, "/prodinfof");
case BisPartitionId::SafeMode:
return GetOrCreateDirectoryRelative(nand_root, "/safe");
case BisPartitionId::System:
return GetOrCreateDirectoryRelative(nand_root, "/system");
case BisPartitionId::User:
return GetOrCreateDirectoryRelative(nand_root, "/user");
default:
return nullptr;
}
}
VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id) const {
Core::Crypto::KeyManager keys;
Core::Crypto::PartitionDataManager pdm{
Core::System::GetInstance().GetFilesystem()->OpenDirectory(
FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), Mode::Read)};
keys.PopulateFromPartitionData(pdm);
switch (id) {
case BisPartitionId::CalibrationBinary:
return pdm.GetDecryptedProdInfo();
case BisPartitionId::BootConfigAndPackage2Part1:
case BisPartitionId::BootConfigAndPackage2Part2:
case BisPartitionId::BootConfigAndPackage2Part3:
case BisPartitionId::BootConfigAndPackage2Part4:
case BisPartitionId::BootConfigAndPackage2Part5:
case BisPartitionId::BootConfigAndPackage2Part6: {
const auto new_id = static_cast<u8>(id) -
static_cast<u8>(BisPartitionId::BootConfigAndPackage2Part1) +
static_cast<u8>(Core::Crypto::Package2Type::NormalMain);
return pdm.GetPackage2Raw(static_cast<Core::Crypto::Package2Type>(new_id));
}
default:
return nullptr;
}
}
VirtualDir BISFactory::GetImageDirectory() const {
return GetOrCreateDirectoryRelative(nand_root, "/user/Album");
}
u64 BISFactory::GetSystemNANDFreeSpace() const {
const auto sys_dir = GetOrCreateDirectoryRelative(nand_root, "/system");
if (sys_dir == nullptr)
return 0;
return GetSystemNANDTotalSpace() - sys_dir->GetSize();
}
u64 BISFactory::GetSystemNANDTotalSpace() const {
return static_cast<u64>(Settings::values.nand_system_size);
}
u64 BISFactory::GetUserNANDFreeSpace() const {
const auto usr_dir = GetOrCreateDirectoryRelative(nand_root, "/user");
if (usr_dir == nullptr)
return 0;
return GetUserNANDTotalSpace() - usr_dir->GetSize();
}
u64 BISFactory::GetUserNANDTotalSpace() const {
return static_cast<u64>(Settings::values.nand_user_size);
}
u64 BISFactory::GetFullNANDTotalSpace() const {
return static_cast<u64>(Settings::values.nand_total_size);
}
VirtualDir BISFactory::GetBCATDirectory(u64 title_id) const {
return GetOrCreateDirectoryRelative(nand_root,
fmt::format("/system/save/bcat/{:016X}", title_id));
}
} // namespace FileSys

View File

@@ -10,25 +10,7 @@
namespace FileSys {
enum class BisPartitionId : u32 {
UserDataRoot = 20,
CalibrationBinary = 27,
CalibrationFile = 28,
BootConfigAndPackage2Part1 = 21,
BootConfigAndPackage2Part2 = 22,
BootConfigAndPackage2Part3 = 23,
BootConfigAndPackage2Part4 = 24,
BootConfigAndPackage2Part5 = 25,
BootConfigAndPackage2Part6 = 26,
SafeMode = 29,
System = 31,
SystemProperEncryption = 32,
SystemProperPartition = 33,
User = 30,
};
class RegisteredCache;
class PlaceholderCache;
/// File system interface to the Built-In Storage
/// This is currently missing accessors to BIS partitions, but seemed like a good place for the NAND
@@ -38,31 +20,12 @@ public:
explicit BISFactory(VirtualDir nand_root, VirtualDir load_root, VirtualDir dump_root);
~BISFactory();
VirtualDir GetSystemNANDContentDirectory() const;
VirtualDir GetUserNANDContentDirectory() const;
RegisteredCache* GetSystemNANDContents() const;
RegisteredCache* GetUserNANDContents() const;
PlaceholderCache* GetSystemNANDPlaceholder() const;
PlaceholderCache* GetUserNANDPlaceholder() const;
VirtualDir GetModificationLoadRoot(u64 title_id) const;
VirtualDir GetModificationDumpRoot(u64 title_id) const;
VirtualDir OpenPartition(BisPartitionId id) const;
VirtualFile OpenPartitionStorage(BisPartitionId id) const;
VirtualDir GetImageDirectory() const;
u64 GetSystemNANDFreeSpace() const;
u64 GetSystemNANDTotalSpace() const;
u64 GetUserNANDFreeSpace() const;
u64 GetUserNANDTotalSpace() const;
u64 GetFullNANDTotalSpace() const;
VirtualDir GetBCATDirectory(u64 title_id) const;
private:
VirtualDir nand_root;
VirtualDir load_root;
@@ -70,9 +33,6 @@ private:
std::unique_ptr<RegisteredCache> sysnand_cache;
std::unique_ptr<RegisteredCache> usrnand_cache;
std::unique_ptr<PlaceholderCache> sysnand_placeholder;
std::unique_ptr<PlaceholderCache> usrnand_placeholder;
};
} // namespace FileSys

View File

@@ -12,16 +12,12 @@
#include "core/file_sys/content_archive.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/partition_filesystem.h"
#include "core/file_sys/romfs.h"
#include "core/file_sys/submission_package.h"
#include "core/file_sys/vfs_concat.h"
#include "core/file_sys/vfs_offset.h"
#include "core/file_sys/vfs_vector.h"
#include "core/loader/loader.h"
namespace FileSys {
constexpr u64 GAMECARD_CERTIFICATE_OFFSET = 0x7000;
constexpr std::array partition_names{
"update",
"normal",
@@ -179,26 +175,6 @@ VirtualDir XCI::GetParentDirectory() const {
return file->GetContainingDirectory();
}
VirtualDir XCI::ConcatenatedPseudoDirectory() {
const auto out = std::make_shared<VectorVfsDirectory>();
for (const auto& part_id : {XCIPartition::Normal, XCIPartition::Logo, XCIPartition::Secure}) {
const auto& part = GetPartition(part_id);
if (part == nullptr)
continue;
for (const auto& file : part->GetFiles())
out->AddFile(file);
}
return out;
}
std::array<u8, 0x200> XCI::GetCertificate() const {
std::array<u8, 0x200> out;
file->Read(out.data(), out.size(), GAMECARD_CERTIFICATE_OFFSET);
return out;
}
Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
const auto partition_index = static_cast<std::size_t>(part);
const auto& partition = partitions[partition_index];

View File

@@ -91,8 +91,6 @@ public:
VirtualDir GetLogoPartition() const;
u64 GetProgramTitleID() const;
u32 GetSystemUpdateVersion();
u64 GetSystemUpdateTitleID() const;
bool HasProgramNCA() const;
VirtualFile GetProgramNCAFile() const;
@@ -108,11 +106,6 @@ public:
VirtualDir GetParentDirectory() const override;
// Creates a directory that contains all the NCAs in the gamecard
VirtualDir ConcatenatedPseudoDirectory();
std::array<u8, 0x200> GetCertificate() const;
private:
Loader::ResultStatus AddNCAFromPartition(XCIPartition part);
@@ -127,8 +120,6 @@ private:
std::shared_ptr<NCA> program;
std::vector<std::shared_ptr<NCA>> ncas;
u64 update_normal_partition_end;
Core::Crypto::KeyManager keys;
};
} // namespace FileSys

View File

@@ -0,0 +1,492 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <locale>
#include "common/hex_util.h"
#include "common/microprofile.h"
#include "common/swap.h"
#include "core/core.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
#include "core/file_sys/cheat_engine.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/hid/controllers/npad.h"
#include "core/hle/service/hid/hid.h"
#include "core/hle/service/sm/sm.h"
namespace FileSys {
constexpr s64 CHEAT_ENGINE_TICKS = static_cast<s64>(Core::Timing::BASE_CLOCK_RATE / 60);
constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF;
u64 Cheat::Address() const {
u64 out;
std::memcpy(&out, raw.data(), sizeof(u64));
return Common::swap64(out) & 0xFFFFFFFFFF;
}
u64 Cheat::ValueWidth(u64 offset) const {
return Value(offset, width);
}
u64 Cheat::Value(u64 offset, u64 width) const {
u64 out;
std::memcpy(&out, raw.data() + offset, sizeof(u64));
out = Common::swap64(out);
if (width == 8)
return out;
return out & ((1ull << (width * CHAR_BIT)) - 1);
}
u32 Cheat::KeypadValue() const {
u32 out;
std::memcpy(&out, raw.data(), sizeof(u32));
return Common::swap32(out) & 0x0FFFFFFF;
}
void CheatList::SetMemoryParameters(VAddr main_begin, VAddr heap_begin, VAddr main_end,
VAddr heap_end, MemoryWriter writer, MemoryReader reader) {
this->main_region_begin = main_begin;
this->main_region_end = main_end;
this->heap_region_begin = heap_begin;
this->heap_region_end = heap_end;
this->writer = writer;
this->reader = reader;
}
MICROPROFILE_DEFINE(Cheat_Engine, "Add-Ons", "Cheat Engine", MP_RGB(70, 200, 70));
void CheatList::Execute() {
MICROPROFILE_SCOPE(Cheat_Engine);
std::fill(scratch.begin(), scratch.end(), 0);
in_standard = false;
for (std::size_t i = 0; i < master_list.size(); ++i) {
LOG_DEBUG(Common_Filesystem, "Executing block #{:08X} ({})", i, master_list[i].first);
current_block = i;
ExecuteBlock(master_list[i].second);
}
in_standard = true;
for (std::size_t i = 0; i < standard_list.size(); ++i) {
LOG_DEBUG(Common_Filesystem, "Executing block #{:08X} ({})", i, standard_list[i].first);
current_block = i;
ExecuteBlock(standard_list[i].second);
}
}
CheatList::CheatList(const Core::System& system_, ProgramSegment master, ProgramSegment standard)
: master_list{std::move(master)}, standard_list{std::move(standard)}, system{&system_} {}
bool CheatList::EvaluateConditional(const Cheat& cheat) const {
using ComparisonFunction = bool (*)(u64, u64);
constexpr std::array<ComparisonFunction, 6> comparison_functions{
[](u64 a, u64 b) { return a > b; }, [](u64 a, u64 b) { return a >= b; },
[](u64 a, u64 b) { return a < b; }, [](u64 a, u64 b) { return a <= b; },
[](u64 a, u64 b) { return a == b; }, [](u64 a, u64 b) { return a != b; },
};
if (cheat.type == CodeType::ConditionalInput) {
const auto applet_resource =
system->ServiceManager().GetService<Service::HID::Hid>("hid")->GetAppletResource();
if (applet_resource == nullptr) {
LOG_WARNING(
Common_Filesystem,
"Attempted to evaluate input conditional, but applet resource is not initialized!");
return false;
}
const auto press_state =
applet_resource
->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)
.GetAndResetPressState();
return ((press_state & cheat.KeypadValue()) & KEYPAD_BITMASK) != 0;
}
ASSERT(cheat.type == CodeType::Conditional);
const auto offset =
cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin;
ASSERT(static_cast<u8>(cheat.comparison_op.Value()) < 6);
auto* function = comparison_functions[static_cast<u8>(cheat.comparison_op.Value())];
const auto addr = cheat.Address() + offset;
return function(reader(cheat.width, SanitizeAddress(addr)), cheat.ValueWidth(8));
}
void CheatList::ProcessBlockPairs(const Block& block) {
block_pairs.clear();
u64 scope = 0;
std::map<u64, u64> pairs;
for (std::size_t i = 0; i < block.size(); ++i) {
const auto& cheat = block[i];
switch (cheat.type) {
case CodeType::Conditional:
case CodeType::ConditionalInput:
pairs.insert_or_assign(scope, i);
++scope;
break;
case CodeType::EndConditional: {
--scope;
const auto idx = pairs.at(scope);
block_pairs.insert_or_assign(idx, i);
break;
}
case CodeType::Loop: {
if (cheat.end_of_loop) {
--scope;
const auto idx = pairs.at(scope);
block_pairs.insert_or_assign(idx, i);
} else {
pairs.insert_or_assign(scope, i);
++scope;
}
break;
}
}
}
}
void CheatList::WriteImmediate(const Cheat& cheat) {
const auto offset =
cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin;
const auto& register_3 = scratch.at(cheat.register_3);
const auto addr = cheat.Address() + offset + register_3;
LOG_DEBUG(Common_Filesystem, "writing value={:016X} to addr={:016X}", addr,
cheat.Value(8, cheat.width));
writer(cheat.width, SanitizeAddress(addr), cheat.ValueWidth(8));
}
void CheatList::BeginConditional(const Cheat& cheat) {
if (EvaluateConditional(cheat)) {
return;
}
const auto iter = block_pairs.find(current_index);
ASSERT(iter != block_pairs.end());
current_index = iter->second - 1;
}
void CheatList::EndConditional(const Cheat& cheat) {
LOG_DEBUG(Common_Filesystem, "Ending conditional block.");
}
void CheatList::Loop(const Cheat& cheat) {
if (cheat.end_of_loop.Value())
ASSERT(!cheat.end_of_loop.Value());
auto& register_3 = scratch.at(cheat.register_3);
const auto iter = block_pairs.find(current_index);
ASSERT(iter != block_pairs.end());
ASSERT(iter->first < iter->second);
const s32 initial_value = static_cast<s32>(cheat.Value(4, sizeof(s32)));
for (s32 i = initial_value; i >= 0; --i) {
register_3 = static_cast<u64>(i);
for (std::size_t c = iter->first + 1; c < iter->second; ++c) {
current_index = c;
ExecuteSingleCheat(
(in_standard ? standard_list : master_list)[current_block].second[c]);
}
}
current_index = iter->second;
}
void CheatList::LoadImmediate(const Cheat& cheat) {
auto& register_3 = scratch.at(cheat.register_3);
LOG_DEBUG(Common_Filesystem, "setting register={:01X} equal to value={:016X}", cheat.register_3,
cheat.Value(4, 8));
register_3 = cheat.Value(4, 8);
}
void CheatList::LoadIndexed(const Cheat& cheat) {
const auto offset =
cheat.memory_type == MemoryType::MainNSO ? main_region_begin : heap_region_begin;
auto& register_3 = scratch.at(cheat.register_3);
const auto addr = (cheat.load_from_register.Value() ? register_3 : offset) + cheat.Address();
LOG_DEBUG(Common_Filesystem, "writing indexed value to register={:01X}, addr={:016X}",
cheat.register_3, addr);
register_3 = reader(cheat.width, SanitizeAddress(addr));
}
void CheatList::StoreIndexed(const Cheat& cheat) {
const auto& register_3 = scratch.at(cheat.register_3);
const auto addr =
register_3 + (cheat.add_additional_register.Value() ? scratch.at(cheat.register_6) : 0);
LOG_DEBUG(Common_Filesystem, "writing value={:016X} to addr={:016X}",
cheat.Value(4, cheat.width), addr);
writer(cheat.width, SanitizeAddress(addr), cheat.ValueWidth(4));
}
void CheatList::RegisterArithmetic(const Cheat& cheat) {
using ArithmeticFunction = u64 (*)(u64, u64);
constexpr std::array<ArithmeticFunction, 5> arithmetic_functions{
[](u64 a, u64 b) { return a + b; }, [](u64 a, u64 b) { return a - b; },
[](u64 a, u64 b) { return a * b; }, [](u64 a, u64 b) { return a << b; },
[](u64 a, u64 b) { return a >> b; },
};
using ArithmeticOverflowCheck = bool (*)(u64, u64);
constexpr std::array<ArithmeticOverflowCheck, 5> arithmetic_overflow_checks{
[](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() - b); }, // a + b
[](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() + b); }, // a - b
[](u64 a, u64 b) { return a > (std::numeric_limits<u64>::max() / b); }, // a * b
[](u64 a, u64 b) { return b >= 64 || (a & ~((1ull << (64 - b)) - 1)) != 0; }, // a << b
[](u64 a, u64 b) { return b >= 64 || (a & ((1ull << b) - 1)) != 0; }, // a >> b
};
static_assert(sizeof(arithmetic_functions) == sizeof(arithmetic_overflow_checks),
"Missing or have extra arithmetic overflow checks compared to functions!");
auto& register_3 = scratch.at(cheat.register_3);
ASSERT(static_cast<u8>(cheat.arithmetic_op.Value()) < 5);
auto* function = arithmetic_functions[static_cast<u8>(cheat.arithmetic_op.Value())];
auto* overflow_function =
arithmetic_overflow_checks[static_cast<u8>(cheat.arithmetic_op.Value())];
LOG_DEBUG(Common_Filesystem, "performing arithmetic with register={:01X}, value={:016X}",
cheat.register_3, cheat.ValueWidth(4));
if (overflow_function(register_3, cheat.ValueWidth(4))) {
LOG_WARNING(Common_Filesystem,
"overflow will occur when performing arithmetic operation={:02X} with operands "
"a={:016X}, b={:016X}!",
static_cast<u8>(cheat.arithmetic_op.Value()), register_3, cheat.ValueWidth(4));
}
register_3 = function(register_3, cheat.ValueWidth(4));
}
void CheatList::BeginConditionalInput(const Cheat& cheat) {
if (EvaluateConditional(cheat))
return;
const auto iter = block_pairs.find(current_index);
ASSERT(iter != block_pairs.end());
current_index = iter->second - 1;
}
VAddr CheatList::SanitizeAddress(VAddr in) const {
if ((in < main_region_begin || in >= main_region_end) &&
(in < heap_region_begin || in >= heap_region_end)) {
LOG_ERROR(Common_Filesystem,
"Cheat attempting to access memory at invalid address={:016X}, if this persists, "
"the cheat may be incorrect. However, this may be normal early in execution if "
"the game has not properly set up yet.",
in);
return 0; ///< Invalid addresses will hard crash
}
return in;
}
void CheatList::ExecuteSingleCheat(const Cheat& cheat) {
using CheatOperationFunction = void (CheatList::*)(const Cheat&);
constexpr std::array<CheatOperationFunction, 9> cheat_operation_functions{
&CheatList::WriteImmediate, &CheatList::BeginConditional,
&CheatList::EndConditional, &CheatList::Loop,
&CheatList::LoadImmediate, &CheatList::LoadIndexed,
&CheatList::StoreIndexed, &CheatList::RegisterArithmetic,
&CheatList::BeginConditionalInput,
};
const auto index = static_cast<u8>(cheat.type.Value());
ASSERT(index < sizeof(cheat_operation_functions));
const auto op = cheat_operation_functions[index];
(this->*op)(cheat);
}
void CheatList::ExecuteBlock(const Block& block) {
encountered_loops.clear();
ProcessBlockPairs(block);
for (std::size_t i = 0; i < block.size(); ++i) {
current_index = i;
ExecuteSingleCheat(block[i]);
i = current_index;
}
}
CheatParser::~CheatParser() = default;
CheatList CheatParser::MakeCheatList(const Core::System& system, CheatList::ProgramSegment master,
CheatList::ProgramSegment standard) const {
return {system, std::move(master), std::move(standard)};
}
TextCheatParser::~TextCheatParser() = default;
CheatList TextCheatParser::Parse(const Core::System& system, const std::vector<u8>& data) const {
std::stringstream ss;
ss.write(reinterpret_cast<const char*>(data.data()), data.size());
std::vector<std::string> lines;
std::string stream_line;
while (std::getline(ss, stream_line)) {
// Remove a trailing \r
if (!stream_line.empty() && stream_line.back() == '\r')
stream_line.pop_back();
lines.push_back(std::move(stream_line));
}
CheatList::ProgramSegment master_list;
CheatList::ProgramSegment standard_list;
for (std::size_t i = 0; i < lines.size(); ++i) {
auto line = lines[i];
if (!line.empty() && (line[0] == '[' || line[0] == '{')) {
const auto master = line[0] == '{';
const auto begin = master ? line.find('{') : line.find('[');
const auto end = master ? line.rfind('}') : line.rfind(']');
ASSERT(begin != std::string::npos && end != std::string::npos);
const std::string patch_name{line.begin() + begin + 1, line.begin() + end};
CheatList::Block block{};
while (i < lines.size() - 1) {
line = lines[++i];
if (!line.empty() && (line[0] == '[' || line[0] == '{')) {
--i;
break;
}
if (line.size() < 8)
continue;
Cheat out{};
out.raw = ParseSingleLineCheat(line);
block.push_back(out);
}
(master ? master_list : standard_list).emplace_back(patch_name, block);
}
}
return MakeCheatList(system, master_list, standard_list);
}
std::array<u8, 16> TextCheatParser::ParseSingleLineCheat(const std::string& line) const {
std::array<u8, 16> out{};
if (line.size() < 8)
return out;
const auto word1 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data(), 8});
std::memcpy(out.data(), word1.data(), sizeof(u32));
if (line.size() < 17 || line[8] != ' ')
return out;
const auto word2 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 9, 8});
std::memcpy(out.data() + sizeof(u32), word2.data(), sizeof(u32));
if (line.size() < 26 || line[17] != ' ') {
// Perform shifting in case value is truncated early.
const auto type = static_cast<CodeType>((out[0] & 0xF0) >> 4);
if (type == CodeType::Loop || type == CodeType::LoadImmediate ||
type == CodeType::StoreIndexed || type == CodeType::RegisterArithmetic) {
std::memcpy(out.data() + 8, out.data() + 4, sizeof(u32));
std::memset(out.data() + 4, 0, sizeof(u32));
}
return out;
}
const auto word3 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 18, 8});
std::memcpy(out.data() + 2 * sizeof(u32), word3.data(), sizeof(u32));
if (line.size() < 35 || line[26] != ' ') {
// Perform shifting in case value is truncated early.
const auto type = static_cast<CodeType>((out[0] & 0xF0) >> 4);
if (type == CodeType::WriteImmediate || type == CodeType::Conditional) {
std::memcpy(out.data() + 12, out.data() + 8, sizeof(u32));
std::memset(out.data() + 8, 0, sizeof(u32));
}
return out;
}
const auto word4 = Common::HexStringToArray<sizeof(u32)>(std::string_view{line.data() + 27, 8});
std::memcpy(out.data() + 3 * sizeof(u32), word4.data(), sizeof(u32));
return out;
}
namespace {
u64 MemoryReadImpl(u32 width, VAddr addr) {
switch (width) {
case 1:
return Memory::Read8(addr);
case 2:
return Memory::Read16(addr);
case 4:
return Memory::Read32(addr);
case 8:
return Memory::Read64(addr);
default:
UNREACHABLE();
return 0;
}
}
void MemoryWriteImpl(u32 width, VAddr addr, u64 value) {
switch (width) {
case 1:
Memory::Write8(addr, static_cast<u8>(value));
break;
case 2:
Memory::Write16(addr, static_cast<u16>(value));
break;
case 4:
Memory::Write32(addr, static_cast<u32>(value));
break;
case 8:
Memory::Write64(addr, value);
break;
default:
UNREACHABLE();
}
}
} // Anonymous namespace
CheatEngine::CheatEngine(Core::System& system, std::vector<CheatList> cheats_,
const std::string& build_id, VAddr code_region_start,
VAddr code_region_end)
: cheats{std::move(cheats_)}, core_timing{system.CoreTiming()} {
event = core_timing.RegisterEvent(
"CheatEngine::FrameCallback::" + build_id,
[this](u64 userdata, s64 cycles_late) { FrameCallback(userdata, cycles_late); });
core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS, event);
const auto& vm_manager = system.CurrentProcess()->VMManager();
for (auto& list : this->cheats) {
list.SetMemoryParameters(code_region_start, vm_manager.GetHeapRegionBaseAddress(),
code_region_end, vm_manager.GetHeapRegionEndAddress(),
&MemoryWriteImpl, &MemoryReadImpl);
}
}
CheatEngine::~CheatEngine() {
core_timing.UnscheduleEvent(event, 0);
}
void CheatEngine::FrameCallback(u64 userdata, s64 cycles_late) {
for (auto& list : cheats) {
list.Execute();
}
core_timing.ScheduleEvent(CHEAT_ENGINE_TICKS - cycles_late, event);
}
} // namespace FileSys

View File

@@ -0,0 +1,234 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <map>
#include <set>
#include <vector>
#include "common/bit_field.h"
#include "common/common_types.h"
namespace Core {
class System;
}
namespace Core::Timing {
class CoreTiming;
struct EventType;
} // namespace Core::Timing
namespace FileSys {
enum class CodeType : u32 {
// 0TMR00AA AAAAAAAA YYYYYYYY YYYYYYYY
// Writes a T sized value Y to the address A added to the value of register R in memory domain M
WriteImmediate = 0,
// 1TMC00AA AAAAAAAA YYYYYYYY YYYYYYYY
// Compares the T sized value Y to the value at address A in memory domain M using the
// conditional function C. If success, continues execution. If failure, jumps to the matching
// EndConditional statement.
Conditional = 1,
// 20000000
// Terminates a Conditional or ConditionalInput block.
EndConditional = 2,
// 300R0000 VVVVVVVV
// Starts looping V times, storing the current count in register R.
// Loop block is terminated with a matching 310R0000.
Loop = 3,
// 400R0000 VVVVVVVV VVVVVVVV
// Sets the value of register R to the value V.
LoadImmediate = 4,
// 5TMRI0AA AAAAAAAA
// Sets the value of register R to the value of width T at address A in memory domain M, with
// the current value of R added to the address if I == 1.
LoadIndexed = 5,
// 6T0RIFG0 VVVVVVVV VVVVVVVV
// Writes the value V of width T to the memory address stored in register R. Adds the value of
// register G to the final calculation if F is nonzero. Increments the value of register R by T
// after operation if I is nonzero.
StoreIndexed = 6,
// 7T0RA000 VVVVVVVV
// Performs the arithmetic operation A on the value in register R and the value V of width T,
// storing the result in register R.
RegisterArithmetic = 7,
// 8KKKKKKK
// Checks to see if any of the buttons defined by the bitmask K are pressed. If any are,
// execution continues. If none are, execution skips to the next EndConditional command.
ConditionalInput = 8,
};
enum class MemoryType : u32 {
// Addressed relative to start of main NSO
MainNSO = 0,
// Addressed relative to start of heap
Heap = 1,
};
enum class ArithmeticOp : u32 {
Add = 0,
Sub = 1,
Mult = 2,
LShift = 3,
RShift = 4,
};
enum class ComparisonOp : u32 {
GreaterThan = 1,
GreaterThanEqual = 2,
LessThan = 3,
LessThanEqual = 4,
Equal = 5,
Inequal = 6,
};
union Cheat {
std::array<u8, 16> raw;
BitField<4, 4, CodeType> type;
BitField<0, 4, u32> width; // Can be 1, 2, 4, or 8. Measured in bytes.
BitField<0, 4, u32> end_of_loop;
BitField<12, 4, MemoryType> memory_type;
BitField<8, 4, u32> register_3;
BitField<8, 4, ComparisonOp> comparison_op;
BitField<20, 4, u32> load_from_register;
BitField<20, 4, u32> increment_register;
BitField<20, 4, ArithmeticOp> arithmetic_op;
BitField<16, 4, u32> add_additional_register;
BitField<28, 4, u32> register_6;
u64 Address() const;
u64 ValueWidth(u64 offset) const;
u64 Value(u64 offset, u64 width) const;
u32 KeypadValue() const;
};
class CheatParser;
// Represents a full collection of cheats for a game. The Execute function should be called every
// interval that all cheats should be executed. Clients should not directly instantiate this class
// (hence private constructor), they should instead receive an instance from CheatParser, which
// guarantees the list is always in an acceptable state.
class CheatList {
public:
friend class CheatParser;
using Block = std::vector<Cheat>;
using ProgramSegment = std::vector<std::pair<std::string, Block>>;
// (width in bytes, address, value)
using MemoryWriter = void (*)(u32, VAddr, u64);
// (width in bytes, address) -> value
using MemoryReader = u64 (*)(u32, VAddr);
void SetMemoryParameters(VAddr main_begin, VAddr heap_begin, VAddr main_end, VAddr heap_end,
MemoryWriter writer, MemoryReader reader);
void Execute();
private:
CheatList(const Core::System& system_, ProgramSegment master, ProgramSegment standard);
void ProcessBlockPairs(const Block& block);
void ExecuteSingleCheat(const Cheat& cheat);
void ExecuteBlock(const Block& block);
bool EvaluateConditional(const Cheat& cheat) const;
// Individual cheat operations
void WriteImmediate(const Cheat& cheat);
void BeginConditional(const Cheat& cheat);
void EndConditional(const Cheat& cheat);
void Loop(const Cheat& cheat);
void LoadImmediate(const Cheat& cheat);
void LoadIndexed(const Cheat& cheat);
void StoreIndexed(const Cheat& cheat);
void RegisterArithmetic(const Cheat& cheat);
void BeginConditionalInput(const Cheat& cheat);
VAddr SanitizeAddress(VAddr in) const;
// Master Codes are defined as codes that cannot be disabled and are run prior to all
// others.
ProgramSegment master_list;
// All other codes
ProgramSegment standard_list;
bool in_standard = false;
// 16 (0x0-0xF) scratch registers that can be used by cheats
std::array<u64, 16> scratch{};
MemoryWriter writer = nullptr;
MemoryReader reader = nullptr;
u64 main_region_begin{};
u64 heap_region_begin{};
u64 main_region_end{};
u64 heap_region_end{};
u64 current_block{};
// The current index of the cheat within the current Block
u64 current_index{};
// The 'stack' of the program. When a conditional or loop statement is encountered, its index is
// pushed onto this queue. When a end block is encountered, the condition is checked.
std::map<u64, u64> block_pairs;
std::set<u64> encountered_loops;
const Core::System* system;
};
// Intermediary class that parses a text file or other disk format for storing cheats into a
// CheatList object, that can be used for execution.
class CheatParser {
public:
virtual ~CheatParser();
virtual CheatList Parse(const Core::System& system, const std::vector<u8>& data) const = 0;
protected:
CheatList MakeCheatList(const Core::System& system_, CheatList::ProgramSegment master,
CheatList::ProgramSegment standard) const;
};
// CheatParser implementation that parses text files
class TextCheatParser final : public CheatParser {
public:
~TextCheatParser() override;
CheatList Parse(const Core::System& system, const std::vector<u8>& data) const override;
private:
std::array<u8, 16> ParseSingleLineCheat(const std::string& line) const;
};
// Class that encapsulates a CheatList and manages its interaction with memory and CoreTiming
class CheatEngine final {
public:
CheatEngine(Core::System& system_, std::vector<CheatList> cheats_, const std::string& build_id,
VAddr code_region_start, VAddr code_region_end);
~CheatEngine();
private:
void FrameCallback(u64 userdata, s64 cycles_late);
std::vector<CheatList> cheats;
Core::Timing::EventType* event;
Core::Timing::CoreTiming& core_timing;
};
} // namespace FileSys

View File

@@ -528,14 +528,6 @@ u64 NCA::GetTitleId() const {
return header.title_id;
}
std::array<u8, 16> NCA::GetRightsId() const {
return header.rights_id;
}
u32 NCA::GetSDKVersion() const {
return header.sdk_version;
}
bool NCA::IsUpdate() const {
return is_update;
}

View File

@@ -112,8 +112,6 @@ public:
NCAContentType GetType() const;
u64 GetTitleId() const;
std::array<u8, 0x10> GetRightsId() const;
u32 GetSDKVersion() const;
bool IsUpdate() const;
VirtualFile GetRomFS() const;

View File

@@ -22,7 +22,6 @@
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h"
#include "core/loader/nso.h"
#include "core/memory/cheat_engine.h"
#include "core/settings.h"
namespace FileSys {
@@ -64,8 +63,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
if (Settings::values.dump_exefs) {
LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id);
const auto dump_dir =
Core::System::GetInstance().GetFileSystemController().GetModificationDumpRoot(title_id);
const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id);
if (dump_dir != nullptr) {
const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs");
VfsRawCopyD(exefs, exefs_dir);
@@ -90,8 +88,7 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
}
// LayeredExeFS
const auto load_dir =
Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
if (load_dir != nullptr && load_dir->GetSize() > 0) {
auto patch_dirs = load_dir->GetSubdirectories();
std::sort(
@@ -177,8 +174,7 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
if (Settings::values.dump_nso) {
LOG_INFO(Loader, "Dumping NSO for name={}, build_id={}, title_id={:016X}", name, build_id,
title_id);
const auto dump_dir =
Core::System::GetInstance().GetFileSystemController().GetModificationDumpRoot(title_id);
const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id);
if (dump_dir != nullptr) {
const auto nso_dir = GetOrCreateDirectoryRelative(dump_dir, "/nso");
const auto file = nso_dir->CreateFile(fmt::format("{}-{}.nso", name, build_id));
@@ -190,13 +186,7 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id);
const auto load_dir =
Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
if (load_dir == nullptr) {
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
return nso;
}
const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
auto patch_dirs = load_dir->GetSubdirectories();
std::sort(patch_dirs.begin(), patch_dirs.end(),
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
@@ -234,13 +224,7 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
LOG_INFO(Loader, "Querying NSO patch existence for build_id={}", build_id);
const auto load_dir =
Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
if (load_dir == nullptr) {
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
return false;
}
const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
auto patch_dirs = load_dir->GetSubdirectories();
std::sort(patch_dirs.begin(), patch_dirs.end(),
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
@@ -248,10 +232,9 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
return !CollectPatches(patch_dirs, build_id).empty();
}
namespace {
std::optional<std::vector<Memory::CheatEntry>> ReadCheatFileFromFolder(
const Core::System& system, u64 title_id, const std::array<u8, 0x20>& build_id_,
const VirtualDir& base_path, bool upper) {
static std::optional<CheatList> ReadCheatFileFromFolder(const Core::System& system, u64 title_id,
const std::array<u8, 0x20>& build_id_,
const VirtualDir& base_path, bool upper) {
const auto build_id_raw = Common::HexToString(build_id_, upper);
const auto build_id = build_id_raw.substr(0, sizeof(u64) * 2);
const auto file = base_path->GetFile(fmt::format("{}.txt", build_id));
@@ -269,39 +252,31 @@ std::optional<std::vector<Memory::CheatEntry>> ReadCheatFileFromFolder(
return std::nullopt;
}
Memory::TextCheatParser parser;
return parser.Parse(
system, std::string_view(reinterpret_cast<const char* const>(data.data()), data.size()));
TextCheatParser parser;
return parser.Parse(system, data);
}
} // Anonymous namespace
std::vector<Memory::CheatEntry> PatchManager::CreateCheatList(
const Core::System& system, const std::array<u8, 32>& build_id_) const {
const auto load_dir = system.GetFileSystemController().GetModificationLoadRoot(title_id);
if (load_dir == nullptr) {
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
return {};
}
std::vector<CheatList> PatchManager::CreateCheatList(const Core::System& system,
const std::array<u8, 32>& build_id_) const {
const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
auto patch_dirs = load_dir->GetSubdirectories();
std::sort(patch_dirs.begin(), patch_dirs.end(),
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
std::vector<Memory::CheatEntry> out;
std::vector<CheatList> out;
out.reserve(patch_dirs.size());
for (const auto& subdir : patch_dirs) {
auto cheats_dir = subdir->GetSubdirectory("cheats");
if (cheats_dir != nullptr) {
auto res = ReadCheatFileFromFolder(system, title_id, build_id_, cheats_dir, true);
if (res.has_value()) {
std::copy(res->begin(), res->end(), std::back_inserter(out));
out.push_back(std::move(*res));
continue;
}
res = ReadCheatFileFromFolder(system, title_id, build_id_, cheats_dir, false);
if (res.has_value()) {
std::copy(res->begin(), res->end(), std::back_inserter(out));
}
if (res.has_value())
out.push_back(std::move(*res));
}
}
@@ -309,8 +284,7 @@ std::vector<Memory::CheatEntry> PatchManager::CreateCheatList(
}
static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) {
const auto load_dir =
Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
if ((type != ContentRecordType::Program && type != ContentRecordType::Data) ||
load_dir == nullptr || load_dir->GetSize() <= 0) {
return;
@@ -419,8 +393,6 @@ static bool IsDirValidAndNonEmpty(const VirtualDir& dir) {
std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames(
VirtualFile update_raw) const {
if (title_id == 0)
return {};
std::map<std::string, std::string, std::less<>> out;
const auto& installed = Core::System::GetInstance().GetContentProvider();
const auto& disabled = Settings::values.disabled_addons[title_id];
@@ -451,8 +423,7 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
}
// General Mods (LayeredFS and IPS)
const auto mod_dir =
Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
const auto mod_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
if (mod_dir != nullptr && mod_dir->GetSize() > 0) {
for (const auto& mod : mod_dir->GetSubdirectories()) {
std::string types;

View File

@@ -8,9 +8,9 @@
#include <memory>
#include <string>
#include "common/common_types.h"
#include "core/file_sys/cheat_engine.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/vfs.h"
#include "core/memory/dmnt_cheat_types.h"
namespace Core {
class System;
@@ -51,8 +51,8 @@ public:
bool HasNSOPatch(const std::array<u8, 0x20>& build_id) const;
// Creates a CheatList object with all
std::vector<Memory::CheatEntry> CreateCheatList(const Core::System& system,
const std::array<u8, 0x20>& build_id) const;
std::vector<CheatList> CreateCheatList(const Core::System& system,
const std::array<u8, 0x20>& build_id) const;
// Currently tracked RomFS patches:
// - Game Updates

View File

@@ -3,7 +3,6 @@
// Refer to the license.txt file included.
#include <algorithm>
#include <random>
#include <regex>
#include <mbedtls/sha256.h>
#include "common/assert.h"
@@ -49,21 +48,18 @@ static bool FollowsTwoDigitDirFormat(std::string_view name) {
static bool FollowsNcaIdFormat(std::string_view name) {
static const std::regex nca_id_regex("[0-9A-F]{32}\\.nca", std::regex_constants::ECMAScript |
std::regex_constants::icase);
static const std::regex nca_id_cnmt_regex(
"[0-9A-F]{32}\\.cnmt.nca", std::regex_constants::ECMAScript | std::regex_constants::icase);
return (name.size() == 36 && std::regex_match(name.begin(), name.end(), nca_id_regex)) ||
(name.size() == 41 && std::regex_match(name.begin(), name.end(), nca_id_cnmt_regex));
return name.size() == 36 && std::regex_match(name.begin(), name.end(), nca_id_regex);
}
static std::string GetRelativePathFromNcaID(const std::array<u8, 16>& nca_id, bool second_hex_upper,
bool within_two_digit, bool cnmt_suffix) {
if (!within_two_digit)
return fmt::format(cnmt_suffix ? "{}.cnmt.nca" : "/{}.nca",
Common::HexToString(nca_id, second_hex_upper));
bool within_two_digit) {
if (!within_two_digit) {
return fmt::format("/{}.nca", Common::HexToString(nca_id, second_hex_upper));
}
Core::Crypto::SHA256Hash hash{};
mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0);
return fmt::format(cnmt_suffix ? "/000000{:02X}/{}.cnmt.nca" : "/000000{:02X}/{}.nca", hash[0],
return fmt::format("/000000{:02X}/{}.nca", hash[0],
Common::HexToString(nca_id, second_hex_upper));
}
@@ -131,156 +127,6 @@ std::vector<ContentProviderEntry> ContentProvider::ListEntries() const {
return ListEntriesFilter(std::nullopt, std::nullopt, std::nullopt);
}
PlaceholderCache::PlaceholderCache(VirtualDir dir_) : dir(std::move(dir_)) {}
bool PlaceholderCache::Create(const NcaID& id, u64 size) const {
const auto path = GetRelativePathFromNcaID(id, false, true, false);
if (dir->GetFileRelative(path) != nullptr) {
return false;
}
Core::Crypto::SHA256Hash hash{};
mbedtls_sha256(id.data(), id.size(), hash.data(), 0);
const auto dirname = fmt::format("000000{:02X}", hash[0]);
const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname);
if (dir2 == nullptr)
return false;
const auto file = dir2->CreateFile(fmt::format("{}.nca", Common::HexToString(id, false)));
if (file == nullptr)
return false;
return file->Resize(size);
}
bool PlaceholderCache::Delete(const NcaID& id) const {
const auto path = GetRelativePathFromNcaID(id, false, true, false);
if (dir->GetFileRelative(path) == nullptr) {
return false;
}
Core::Crypto::SHA256Hash hash{};
mbedtls_sha256(id.data(), id.size(), hash.data(), 0);
const auto dirname = fmt::format("000000{:02X}", hash[0]);
const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname);
const auto res = dir2->DeleteFile(fmt::format("{}.nca", Common::HexToString(id, false)));
return res;
}
bool PlaceholderCache::Exists(const NcaID& id) const {
const auto path = GetRelativePathFromNcaID(id, false, true, false);
return dir->GetFileRelative(path) != nullptr;
}
bool PlaceholderCache::Write(const NcaID& id, u64 offset, const std::vector<u8>& data) const {
const auto path = GetRelativePathFromNcaID(id, false, true, false);
const auto file = dir->GetFileRelative(path);
if (file == nullptr)
return false;
return file->WriteBytes(data, offset) == data.size();
}
bool PlaceholderCache::Register(RegisteredCache* cache, const NcaID& placeholder,
const NcaID& install) const {
const auto path = GetRelativePathFromNcaID(placeholder, false, true, false);
const auto file = dir->GetFileRelative(path);
if (file == nullptr)
return false;
const auto res = cache->RawInstallNCA(NCA{file}, &VfsRawCopy, false, install);
if (res != InstallResult::Success)
return false;
return Delete(placeholder);
}
bool PlaceholderCache::CleanAll() const {
return dir->GetParentDirectory()->CleanSubdirectoryRecursive(dir->GetName());
}
std::optional<std::array<u8, 0x10>> PlaceholderCache::GetRightsID(const NcaID& id) const {
const auto path = GetRelativePathFromNcaID(id, false, true, false);
const auto file = dir->GetFileRelative(path);
if (file == nullptr)
return std::nullopt;
NCA nca{file};
if (nca.GetStatus() != Loader::ResultStatus::Success &&
nca.GetStatus() != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) {
return std::nullopt;
}
const auto rights_id = nca.GetRightsId();
if (rights_id == NcaID{})
return std::nullopt;
return rights_id;
}
u64 PlaceholderCache::Size(const NcaID& id) const {
const auto path = GetRelativePathFromNcaID(id, false, true, false);
const auto file = dir->GetFileRelative(path);
if (file == nullptr)
return 0;
return file->GetSize();
}
bool PlaceholderCache::SetSize(const NcaID& id, u64 new_size) const {
const auto path = GetRelativePathFromNcaID(id, false, true, false);
const auto file = dir->GetFileRelative(path);
if (file == nullptr)
return false;
return file->Resize(new_size);
}
std::vector<NcaID> PlaceholderCache::List() const {
std::vector<NcaID> out;
for (const auto& sdir : dir->GetSubdirectories()) {
for (const auto& file : sdir->GetFiles()) {
const auto name = file->GetName();
if (name.length() == 36 && name[32] == '.' && name[33] == 'n' && name[34] == 'c' &&
name[35] == 'a') {
out.push_back(Common::HexStringToArray<0x10>(name.substr(0, 32)));
}
}
}
return out;
}
NcaID PlaceholderCache::Generate() {
std::random_device device;
std::mt19937 gen(device());
std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
NcaID out{};
const auto v1 = distribution(gen);
const auto v2 = distribution(gen);
std::memcpy(out.data(), &v1, sizeof(u64));
std::memcpy(out.data() + sizeof(u64), &v2, sizeof(u64));
return out;
}
VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
std::string_view path) const {
const auto file = dir->GetFileRelative(path);
@@ -323,18 +169,14 @@ VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
VirtualFile RegisteredCache::GetFileAtID(NcaID id) const {
VirtualFile file;
// Try all five relevant modes of file storage:
// (bit 2 = uppercase/lower, bit 1 = within a two-digit dir, bit 0 = .cnmt suffix)
// 000: /000000**/{:032X}.nca
// 010: /{:032X}.nca
// 100: /000000**/{:032x}.nca
// 110: /{:032x}.nca
// 111: /{:032x}.cnmt.nca
for (u8 i = 0; i < 8; ++i) {
if ((i % 2) == 1 && i != 7)
continue;
const auto path =
GetRelativePathFromNcaID(id, (i & 0b100) == 0, (i & 0b010) == 0, (i & 0b001) == 0b001);
// Try all four modes of file storage:
// (bit 1 = uppercase/lower, bit 0 = within a two-digit dir)
// 00: /000000**/{:032X}.nca
// 01: /{:032X}.nca
// 10: /000000**/{:032x}.nca
// 11: /{:032x}.nca
for (u8 i = 0; i < 4; ++i) {
const auto path = GetRelativePathFromNcaID(id, (i & 0b10) == 0, (i & 0b01) == 0);
file = OpenFileOrDirectoryConcat(dir, path);
if (file != nullptr)
return file;
@@ -630,7 +472,7 @@ InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFuncti
memcpy(id.data(), hash.data(), 16);
}
std::string path = GetRelativePathFromNcaID(id, false, true, false);
std::string path = GetRelativePathFromNcaID(id, false, true);
if (GetFileAtID(id) != nullptr && !overwrite_if_exists) {
LOG_WARNING(Loader, "Attempting to overwrite existing NCA. Skipping...");

View File

@@ -25,8 +25,6 @@ enum class NCAContentType : u8;
enum class TitleType : u8;
struct ContentRecord;
struct MetaRecord;
class RegisteredCache;
using NcaID = std::array<u8, 0x10>;
using ContentProviderParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>;
@@ -91,27 +89,6 @@ protected:
Core::Crypto::KeyManager keys;
};
class PlaceholderCache {
public:
explicit PlaceholderCache(VirtualDir dir);
bool Create(const NcaID& id, u64 size) const;
bool Delete(const NcaID& id) const;
bool Exists(const NcaID& id) const;
bool Write(const NcaID& id, u64 offset, const std::vector<u8>& data) const;
bool Register(RegisteredCache* cache, const NcaID& placeholder, const NcaID& install) const;
bool CleanAll() const;
std::optional<std::array<u8, 0x10>> GetRightsID(const NcaID& id) const;
u64 Size(const NcaID& id) const;
bool SetSize(const NcaID& id, u64 new_size) const;
std::vector<NcaID> List() const;
static NcaID Generate();
private:
VirtualDir dir;
};
/*
* A class that catalogues NCAs in the registered directory structure.
* Nintendo's registered format follows this structure:
@@ -126,8 +103,6 @@ private:
* when 4GB splitting can be ignored.)
*/
class RegisteredCache : public ContentProvider {
friend class PlaceholderCache;
public:
// Parsing function defines the conversion from raw file to NCA. If there are other steps
// besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom

View File

@@ -7,7 +7,6 @@
#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/file_sys/card_image.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/patch_manager.h"
@@ -35,7 +34,7 @@ void RomFSFactory::SetPackedUpdate(VirtualFile update_raw) {
this->update_raw = std::move(update_raw);
}
ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() const {
ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() {
if (!updatable)
return MakeResult<VirtualFile>(file);
@@ -44,8 +43,7 @@ ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() const {
patch_manager.PatchRomFS(file, ivfc_offset, ContentRecordType::Program, update_raw));
}
ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage,
ContentRecordType type) const {
ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, ContentRecordType type) {
std::shared_ptr<NCA> res;
switch (storage) {
@@ -53,17 +51,13 @@ ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage,
res = Core::System::GetInstance().GetContentProvider().GetEntry(title_id, type);
break;
case StorageId::NandSystem:
res =
Core::System::GetInstance().GetFileSystemController().GetSystemNANDContents()->GetEntry(
title_id, type);
res = Service::FileSystem::GetSystemNANDContents()->GetEntry(title_id, type);
break;
case StorageId::NandUser:
res = Core::System::GetInstance().GetFileSystemController().GetUserNANDContents()->GetEntry(
title_id, type);
res = Service::FileSystem::GetUserNANDContents()->GetEntry(title_id, type);
break;
case StorageId::SdCard:
res = Core::System::GetInstance().GetFileSystemController().GetSDMCContents()->GetEntry(
title_id, type);
res = Service::FileSystem::GetSDMCContents()->GetEntry(title_id, type);
break;
default:
UNIMPLEMENTED_MSG("Unimplemented storage_id={:02X}", static_cast<u8>(storage));

View File

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

View File

@@ -15,8 +15,22 @@ namespace FileSys {
constexpr char SAVE_DATA_SIZE_FILENAME[] = ".yuzu_save_size";
namespace {
void PrintSaveDataDescriptorWarnings(SaveDataDescriptor meta) {
std::string SaveDataDescriptor::DebugInfo() const {
return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}, "
"rank={}, index={}]",
static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id,
static_cast<u8>(rank), index);
}
SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {
// Delete all temporary storages
// On hardware, it is expected that temporary storage be empty at first use.
dir->DeleteSubdirectoryRecursive("temp");
}
SaveDataFactory::~SaveDataFactory() = default;
ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataDescriptor& meta) {
if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) {
if (meta.zero_1 != 0) {
LOG_WARNING(Service_FS,
@@ -51,51 +65,23 @@ void PrintSaveDataDescriptorWarnings(SaveDataDescriptor meta) {
"non-zero ({:016X}{:016X})",
meta.user_id[1], meta.user_id[0]);
}
}
} // Anonymous namespace
std::string SaveDataDescriptor::DebugInfo() const {
return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, "
"save_id={:016X}, "
"rank={}, index={}]",
static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id,
static_cast<u8>(rank), index);
}
SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {
// Delete all temporary storages
// On hardware, it is expected that temporary storage be empty at first use.
dir->DeleteSubdirectoryRecursive("temp");
}
SaveDataFactory::~SaveDataFactory() = default;
ResultVal<VirtualDir> SaveDataFactory::Create(SaveDataSpaceId space,
const SaveDataDescriptor& meta) const {
PrintSaveDataDescriptorWarnings(meta);
const auto save_directory =
std::string save_directory =
GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id);
auto out = dir->CreateDirectoryRelative(save_directory);
// Return an error if the save data doesn't actually exist.
if (out == nullptr) {
// TODO(DarkLordZach): Find out correct error code.
return ResultCode(-1);
}
return MakeResult<VirtualDir>(std::move(out));
}
ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space,
const SaveDataDescriptor& meta) const {
const auto save_directory =
GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id);
// TODO(DarkLordZach): Try to not create when opening, there are dedicated create save methods.
// But, user_ids don't match so this works for now.
auto out = dir->GetDirectoryRelative(save_directory);
if (out == nullptr) {
// TODO(bunnei): This is a work-around to always create a save data directory if it does not
// already exist. This is a hack, as we do not understand yet how this works on hardware.
// Without a save data directory, many games will assert on boot. This should not have any
// bad side-effects.
out = dir->CreateDirectoryRelative(save_directory);
}
// Return an error if the save data doesn't actually exist.
if (out == nullptr) {
// TODO(Subv): Find out correct error code.
@@ -166,7 +152,7 @@ SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
}
void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
SaveDataSize new_value) const {
SaveDataSize new_value) {
const auto path = GetFullPath(SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
const auto dir = GetOrCreateDirectoryRelative(this->dir, path);

View File

@@ -64,8 +64,7 @@ public:
explicit SaveDataFactory(VirtualDir dir);
~SaveDataFactory();
ResultVal<VirtualDir> Create(SaveDataSpaceId space, const SaveDataDescriptor& meta) const;
ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataDescriptor& meta) const;
ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataDescriptor& meta);
VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const;
@@ -74,8 +73,7 @@ public:
u128 user_id, u64 save_id);
SaveDataSize ReadSaveDataSize(SaveDataType type, u64 title_id, u128 user_id) const;
void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
SaveDataSize new_value) const;
void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, SaveDataSize new_value);
private:
VirtualDir dir;

View File

@@ -6,7 +6,6 @@
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/sdmc_factory.h"
#include "core/file_sys/xts_archive.h"
#include "core/settings.h"
namespace FileSys {
@@ -15,38 +14,16 @@ SDMCFactory::SDMCFactory(VirtualDir dir_)
GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/registered"),
[](const VirtualFile& file, const NcaID& id) {
return NAX{file, id}.GetDecrypted();
})),
placeholder(std::make_unique<PlaceholderCache>(
GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/placehld"))) {}
})) {}
SDMCFactory::~SDMCFactory() = default;
ResultVal<VirtualDir> SDMCFactory::Open() const {
ResultVal<VirtualDir> SDMCFactory::Open() {
return MakeResult<VirtualDir>(dir);
}
VirtualDir SDMCFactory::GetSDMCContentDirectory() const {
return GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents");
}
RegisteredCache* SDMCFactory::GetSDMCContents() const {
return contents.get();
}
PlaceholderCache* SDMCFactory::GetSDMCPlaceholder() const {
return placeholder.get();
}
VirtualDir SDMCFactory::GetImageDirectory() const {
return GetOrCreateDirectoryRelative(dir, "/Nintendo/Album");
}
u64 SDMCFactory::GetSDMCFreeSpace() const {
return GetSDMCTotalSpace() - dir->GetSize();
}
u64 SDMCFactory::GetSDMCTotalSpace() const {
return static_cast<u64>(Settings::values.sdmc_size);
}
} // namespace FileSys

View File

@@ -11,7 +11,6 @@
namespace FileSys {
class RegisteredCache;
class PlaceholderCache;
/// File system interface to the SDCard archive
class SDMCFactory {
@@ -19,23 +18,13 @@ public:
explicit SDMCFactory(VirtualDir dir);
~SDMCFactory();
ResultVal<VirtualDir> Open() const;
VirtualDir GetSDMCContentDirectory() const;
ResultVal<VirtualDir> Open();
RegisteredCache* GetSDMCContents() const;
PlaceholderCache* GetSDMCPlaceholder() const;
VirtualDir GetImageDirectory() const;
u64 GetSDMCFreeSpace() const;
u64 GetSDMCTotalSpace() const;
private:
VirtualDir dir;
std::unique_ptr<RegisteredCache> contents;
std::unique_ptr<PlaceholderCache> placeholder;
};
} // namespace FileSys

View File

@@ -14,7 +14,6 @@
#include "core/file_sys/content_archive.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/partition_filesystem.h"
#include "core/file_sys/program_metadata.h"
#include "core/file_sys/submission_package.h"
#include "core/loader/loader.h"
@@ -79,10 +78,6 @@ Loader::ResultStatus NSP::GetStatus() const {
}
Loader::ResultStatus NSP::GetProgramStatus(u64 title_id) const {
if (IsExtractedType() && GetExeFS() != nullptr && FileSys::IsDirectoryExeFS(GetExeFS())) {
return Loader::ResultStatus::Success;
}
const auto iter = program_status.find(title_id);
if (iter == program_status.end())
return Loader::ResultStatus::ErrorNSPMissingProgramNCA;
@@ -90,29 +85,12 @@ Loader::ResultStatus NSP::GetProgramStatus(u64 title_id) const {
}
u64 NSP::GetFirstTitleID() const {
if (IsExtractedType()) {
return GetProgramTitleID();
}
if (program_status.empty())
return 0;
return program_status.begin()->first;
}
u64 NSP::GetProgramTitleID() const {
if (IsExtractedType()) {
if (GetExeFS() == nullptr || !IsDirectoryExeFS(GetExeFS())) {
return 0;
}
ProgramMetadata meta;
if (meta.Load(GetExeFS()->GetFile("main.npdm")) == Loader::ResultStatus::Success) {
return meta.GetTitleID();
} else {
return 0;
}
}
const auto out = GetFirstTitleID();
if ((out & 0x800) == 0)
return out;
@@ -124,10 +102,6 @@ u64 NSP::GetProgramTitleID() const {
}
std::vector<u64> NSP::GetTitleIDs() const {
if (IsExtractedType()) {
return {GetProgramTitleID()};
}
std::vector<u64> out;
out.reserve(ncas.size());
for (const auto& kv : ncas)
@@ -248,8 +222,7 @@ void NSP::InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files) {
void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
for (const auto& outer_file : files) {
if (outer_file->GetName().size() < 9 ||
outer_file->GetName().substr(outer_file->GetName().size() - 9) != ".cnmt.nca") {
if (outer_file->GetName().substr(outer_file->GetName().size() - 9) != ".cnmt.nca") {
continue;
}

View File

@@ -1,46 +0,0 @@
// Copyright 2019 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/file_sys/system_archive/mii_model.h"
#include "core/file_sys/vfs_vector.h"
namespace FileSys::SystemArchive {
namespace MiiModelData {
constexpr std::array<u8, 0x10> NFTR_STANDARD{'N', 'F', 'T', 'R', 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
constexpr std::array<u8, 0x10> NFSR_STANDARD{'N', 'F', 'S', 'R', 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
constexpr auto TEXTURE_LOW_LINEAR = NFTR_STANDARD;
constexpr auto TEXTURE_LOW_SRGB = NFTR_STANDARD;
constexpr auto TEXTURE_MID_LINEAR = NFTR_STANDARD;
constexpr auto TEXTURE_MID_SRGB = NFTR_STANDARD;
constexpr auto SHAPE_HIGH = NFSR_STANDARD;
constexpr auto SHAPE_MID = NFSR_STANDARD;
} // namespace MiiModelData
VirtualDir MiiModel() {
auto out = std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{},
std::vector<VirtualDir>{}, "data");
out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_LOW_LINEAR.size()>>(
MiiModelData::TEXTURE_LOW_LINEAR, "NXTextureLowLinear.dat"));
out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_LOW_SRGB.size()>>(
MiiModelData::TEXTURE_LOW_SRGB, "NXTextureLowSRGB.dat"));
out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_MID_LINEAR.size()>>(
MiiModelData::TEXTURE_MID_LINEAR, "NXTextureMidLinear.dat"));
out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_MID_SRGB.size()>>(
MiiModelData::TEXTURE_MID_SRGB, "NXTextureMidSRGB.dat"));
out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::SHAPE_HIGH.size()>>(
MiiModelData::SHAPE_HIGH, "ShapeHigh.dat"));
out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::SHAPE_MID.size()>>(
MiiModelData::SHAPE_MID, "ShapeMid.dat"));
return std::move(out);
}
} // namespace FileSys::SystemArchive

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