Compare commits

...

188 commits

Author SHA1 Message Date
Jack cfb826ca6c Update libtoolchain to v0.8.0 (fixes VS dup issue) 2024-10-14 10:20:20 +09:00
jakcron cfadea2994 Fix bug where KeyAreaEncryptionKeys were not calcd 2024-09-22 23:46:52 +08:00
Jack 69a5f5ed2f update libpietendo to v0.8.0 2024-08-04 12:22:20 +08:00
Jack 8798776405
Merge pull request #112 from jakcron/feature-106-multipletickets
Add support for specifying multiple tickets / title keys
2024-01-20 14:01:37 +08:00
Jack 85dd51f496 Skip bad title.keys rows instead of throwing an exception. 2024-01-20 11:20:56 +08:00
Jack 811334bae5 Misc 2024-01-20 11:17:04 +08:00
Jack 1d680c2098 Update SWITCH_KEYS.md to indicate title.keys is now supported. 2024-01-20 11:16:25 +08:00
Jack e205cb6a4f Change --tik so that it can be invoked multiple times to specify a list of ticket files. Additionally added support for hactool title.keys 2024-01-01 13:22:39 +08:00
Jack 978899780c Test GH Actions tweaks 2023-12-31 22:21:35 +08:00
Jack 4922717ad0 Tweak GH Actions 2023-12-31 22:17:00 +08:00
Jack 17f9298c50 Update libpietendo v0.7.2 2023-12-31 22:11:41 +08:00
Jack 30c8751cfd Update libtoolchain to v0.7.0 2023-12-31 22:11:30 +08:00
Jack 0765ee5502 Update libmbedtls to v2.16.12 2023-12-31 22:10:50 +08:00
Jack 268f56b803 Update liblz4 to v1.9.4 2023-12-31 22:10:38 +08:00
Jack cf4d335be3 Update libfmt to 10.1.1 2023-12-31 19:17:57 +08:00
Jack 25850cd74b Test newer GH Actions Dependencies 2023-12-31 18:15:52 +08:00
Jack 705cf9b4f3 Update makefile to v9 2023-12-31 17:51:19 +08:00
Jack 0d248d1c8e #96 Add instructions for using git submodules 2023-12-31 14:24:19 +08:00
Jack c7fe8bcd0f Fixes #92, skip verifying SaveDataOwnerIdList against ACID as this is never populated. 2023-12-31 14:18:15 +08:00
Jack b4a86fc826 Update libpietendo to v0.7.1 2023-12-31 13:58:42 +08:00
Jack 7abcedb7e5 Fix formatting of NCA Key Area. 2023-05-13 16:45:01 +08:00
Jack bcd4df2eb4 Update libpietendo to v0.7.0 2023-05-13 16:44:46 +08:00
Jack 49e3729d95 Update GitHub actions to reflect new branch names. 2023-05-13 16:43:45 +08:00
Jack 8c284ba98a Reset version to v0.0.0 for development-tip branch. 2023-05-13 16:35:39 +08:00
Jack d87c0fcf1c
Merge pull request #89 from jakcron/v1.7-development
Update NSTool to v1.7.0
2023-01-21 23:14:26 +08:00
Jack e2245c2a9e Add explanation for how to extract Patch NCAs. 2023-01-21 23:05:38 +08:00
Jack 76f1f87c51 Bump version to v1.7.0 2023-01-21 22:51:46 +08:00
Jack 21e48a680b Update makefile to v8 2023-01-21 22:51:21 +08:00
Jack 9d7628a1c9 Fix compiling against libfmt 9.0.0 2023-01-21 22:51:11 +08:00
Jack 85a31c90af Update libtoolchain to v0.6.1 and libpietendo to v0.4.0 2023-01-21 22:43:22 +08:00
Jack 591b270ed4 Update libtoolchain to v0.6 & libfmt v9.0.0 2023-01-21 21:14:50 +08:00
Jack fbf62a4df2 Merge branch 'main' into v1.7-development 2023-01-21 20:42:49 +08:00
Jack df045b5dba Update dependencies 2023-01-21 20:39:18 +08:00
Jack 06b75b41cf [NcaProcess] Properly process HashType_None 2023-01-21 20:10:01 +08:00
Jack 1e81318d54 Add note for future work for compressed/sparse storage 2023-01-21 20:07:42 +08:00
Jack 605b5c70f9 Linting 2023-01-21 20:03:21 +08:00
Jack af41275c80 Add comments for the 3 reader streams in NcaProcess::sPartitionInfo 2023-01-21 19:49:15 +08:00
Jack ccd296d144 Update libpietendo to sagumamugas-bktr 2023-01-15 23:07:22 +08:00
Jack 21e1e33e55
Merge pull request #88 from sagumamugas/nca-patches
Support for nca patches
2023-01-15 22:58:28 +08:00
sagumamugas b5e3fb56b1 Support for nca patches 2023-01-12 23:33:08 +01:00
Jack 80cb738b5a NSTool v1.6.6 2022-11-27 12:37:19 +08:00
Jack 21f8c884f6
Merge pull request #87 from jakcron/v1.6-development
Bug fixes for v1.6
2022-11-27 12:36:31 +08:00
Jack b0a07c061a Fix issue where RomFs with no files could not be processed. 2022-11-27 12:26:44 +08:00
Jack 390b544059 Fixed XCI Header Hey selection. 2022-07-07 20:41:05 +08:00
Jack f17b57d2ed Merge branch 'v1.6-stable' fix URL typo for libpietendo repository 2022-07-02 11:22:33 +08:00
Jack 87459b6f8d Update URL for libpietendo repository 2022-07-02 11:21:31 +08:00
Jack e770a06ad4
Merge pull request #82 from jakcron/v1.6-development
Update NSTool to v1.6.5
2022-07-01 15:45:20 +08:00
Jakcron 26898c7fa3 Include refs to libpietendo in visual studio. 2022-07-01 15:32:50 +08:00
Jack cb46c7c7c3 Update github workflow 2022-06-30 18:41:13 +08:00
Jack 5c1bd2c786 Misc whitespace 2022-06-30 18:36:12 +08:00
Jack ffbcf736d2 Update libpietendo 2022-06-30 18:35:54 +08:00
Jack 4e8fb2d3e2 Bump NSTool version to v1.6.5 2022-06-29 21:20:06 +08:00
Jack 14caec3049 Port code to use new submodules. 2022-06-29 21:19:36 +08:00
Jack 15b082d9d2 Remove submodules for libnintendo* 2022-06-29 21:17:10 +08:00
Jack 10dd907c6e Remove libnintendo* add libpietendo, update libfmt & libtoolchain 2022-06-29 21:14:58 +08:00
Jack 04662e529f
Merge pull request #81 from jakcron/v1.6-stable
Update NSTool to v1.6.4
2022-04-10 15:52:05 +08:00
jakcron 88e56b600d bump version to v1.6.4 2022-04-10 15:42:20 +08:00
jakcron 64528a0956 Update libnintendo-hac(-hb) 2022-04-10 15:41:45 +08:00
Jack ebd86508ff
Merge pull request #79 from jakcron/v1.6-stable
Update NSTool to v1.6.3
2022-04-02 17:13:45 +08:00
jakcron 325cfeb0e8 Misc 2022-04-02 16:45:19 +08:00
jakcron 5230d880e1 Bump version to v1.6.3 2022-04-02 16:42:28 +08:00
jakcron e499e2790e Update libinintendo-hac to fix import bug in RomFsMetaGenerator. 2022-04-02 16:42:09 +08:00
Jack befd2a0ba0
Merge pull request #76 from jakcron/v1.6-stable
Update NSTool to v1.6.2
2022-01-13 12:16:37 +08:00
jakcron 116863767d Bump version. 2022-01-13 12:06:55 +08:00
jakcron e6d12a5d65 Update dependencies. 2022-01-13 12:05:55 +08:00
Jack 276db64e45
Merge pull request #74 from jakcron/v1.6-stable
Update NSTool to v1.6.1
2021-11-14 13:52:25 +08:00
jakcron 7bfc1997cc Fix typo in file type CLI option. 2021-11-14 13:44:26 +08:00
Jack 31f67325f7
Merge pull request #73 from jakcron/v1.6-stable
Update NSTool to v1.6.0
2021-11-14 13:08:09 +08:00
jakcron bf3e43d4fd Misc. 2021-11-14 13:02:37 +08:00
jakcron 2b5742d1cc Misc. 2021-11-14 12:57:20 +08:00
jakcron a1dcf64f8a Update README.md 2021-11-14 12:53:57 +08:00
jakcron 2ab44c8a8c Move advice for encrypted files from README.md to SWITCH_KEYS.md 2021-11-14 12:20:15 +08:00
jakcron 1e2902202b Standardise formatting of RomFs and PartitionFs names. 2021-11-14 07:14:58 +08:00
jakcron d1d8850818 More readme drafting 2021-11-14 07:11:34 +08:00
jakcron 6a94885cee Begin Re-draft of README 2021-11-13 15:54:18 +08:00
jakcron 0cb9608590 Change names for NCA content keys internally. 2021-11-13 15:54:06 +08:00
jakcron 93fc2e29bc Update SWITCH_KEYS.md 2021-11-13 15:53:36 +08:00
jakcron a7d5ae6375 Update dependencies. 2021-11-10 16:22:06 +08:00
jakcron c5f9b8a45c Fix typo in readme 2021-11-10 16:08:33 +08:00
jakcron 9222a7dcb7 Fix typo in build_master.yml 2021-11-10 15:42:38 +08:00
jakcron 20603d1177 Update build_master.yml for macOS arm64 and win x86 builds. 2021-11-10 15:41:02 +08:00
jakcron d0e1229f6d Update dependencies. 2021-11-10 15:40:29 +08:00
jakcron 0249957fad Update makefile. 2021-11-10 15:40:19 +08:00
jakcron d53d8ce786 Update dependencies. 2021-11-10 15:12:28 +08:00
jakcron 4430f06422 Address Ubuntu compiler error/warn. 2021-11-10 15:12:20 +08:00
jakcron e74dadba82 Update makefile. 2021-11-10 15:12:01 +08:00
jakcron 0832c98cc0 Misc. 2021-11-09 20:44:13 +08:00
jakcron 1b27126a91 Revise help text again 2021-11-09 20:23:20 +08:00
jakcron 187cbca7e3 Misc typo. 2021-11-09 19:07:19 +08:00
jakcron c327f605e6 Update build_master.yml 2021-11-09 16:56:57 +08:00
jakcron 293180f76a Clarify BUILDING.md 2021-11-09 16:40:36 +08:00
jakcron e813d7a7a6 Update SWITCH_KEYS.md to what can be imported. 2021-11-09 16:38:42 +08:00
jakcron d1e1d5229b Clarified help text. 2021-11-09 16:30:11 +08:00
jakcron be59f92a2c Change formatting of hex data to exclude ":" between bytes. 2021-11-05 15:31:34 +08:00
jakcron 141477207b Misc 2021-11-05 15:23:58 +08:00
jakcron a594486101 Update dependencies to stable branches. 2021-11-05 15:23:18 +08:00
jakcron ebb3433b4f Remove libfnd from visual studio project files. 2021-11-05 14:37:30 +08:00
jakcron c9242f6ee6 Remove libfnd 2021-11-05 14:30:45 +08:00
jakcron a81a105730 Update libnintendo-hac 2021-11-05 14:19:49 +08:00
jakcron 5829c8078e Update KIP description in help text 2021-11-05 14:11:01 +08:00
jakcron 818885e0e8 Misc 2021-11-05 14:10:16 +08:00
jakcron e8597a8e86 [NcaProcess] Identify Sparse partitions and explain they are not supported. 2021-11-05 13:57:35 +08:00
jakcron 7ce606db2a Update dependencies. 2021-11-05 13:53:07 +08:00
jakcron 9d97e96673 Added missing new-line. 2021-10-29 16:09:24 +08:00
jakcron 635930f850 Remove debug print. 2021-10-28 15:19:39 +08:00
jakcron 0091b8ff99 Merge branch 'v1.6-development' of github.com:jakcron/nstool into v1.6-development 2021-10-27 19:12:31 +08:00
jakcron 6d6f824c3a Remove sparse test code for now. 2021-10-27 19:12:24 +08:00
jakcron a8437dfc8e Misc 2021-10-23 11:52:07 +08:00
jakcron 315dd38b20 Update help text. 2021-10-23 11:51:06 +08:00
jakcron f6b669bb8d Misc 2021-10-23 11:43:07 +08:00
jakcron 18a77e89d0 Update readme. 2021-10-23 11:38:44 +08:00
jakcron c307be9749 Misc 2021-10-23 11:38:33 +08:00
jakcron b827cea828 Fix bug where NCA header key was not imported. 2021-10-23 08:44:21 +08:00
jakcron ebdf0211f4 Pre-load common ES Certificates/PublicKeys. 2021-10-22 20:56:39 +08:00
jakcron be0f5c29e0 Better detect, handle and warn when a custom ticket is malformed. 2021-10-22 04:12:03 +08:00
jakcron 620d0609d6 Show ticket property mask even when empty with verbose output. 2021-10-22 04:11:36 +08:00
jakcron 48afdbbad5 Update libtoolchain 2021-10-19 17:17:56 +08:00
jakcron ac3d1d493e Fix title key decryption & continue SparseLayer research. 2021-10-19 16:22:59 +08:00
jakcron 122350b8e5 Fix bug where writeStreamToStream() would not overwrite an existing file. 2021-10-19 16:20:15 +08:00
jakcron fa3da57dcf Misc 2021-10-16 16:41:21 +08:00
jakcron 42de7f6bdc Begin SparseFs impl 2021-10-16 16:40:14 +08:00
jakcron 289470d2a8 Update readme 2021-10-16 16:01:09 +08:00
jakcron 008c76a82d Rename PkiCertProcess to EsCertProcess 2021-10-16 16:00:17 +08:00
jakcron 836a85f71e Misc 2021-10-16 15:44:06 +08:00
jakcron c6d8b9707b Fix ivfc hash fail bug. 2021-10-16 15:41:45 +08:00
jakcron ac89a8788f Misc formatting issue. 2021-10-16 14:28:35 +08:00
jakcron d4232a1f22 Remove not_ported dir. 2021-10-16 14:13:44 +08:00
jakcron f04c53f21e Port NcaProcess to libtoolchain. 2021-10-16 14:13:11 +08:00
jakcron 83e2403dbc First pass NcaProcess impl 2021-10-15 17:29:29 +08:00
jakcron 91ac1937bf Update libnintendo-hac 2021-10-15 15:58:31 +08:00
jakcron de4b75db37 Misc 2021-10-13 16:17:46 +08:00
jakcron 29aeb8e6af Move NcaProcess 2021-10-13 16:16:05 +08:00
jakcron 6666eb74d7 Enable IniProcess and KipProcess in main() 2021-10-13 16:14:41 +08:00
jakcron 78262bc680 Misc 2021-10-13 15:41:02 +08:00
jakcron defccc6ddf Port IniProcess & KipProcess to libtoolchain. 2021-10-13 15:33:47 +08:00
jakcron afd1c97272 Misc 2021-10-13 13:39:45 +08:00
jakcron 054776b586 Move IniProcess and KipProcess 2021-10-12 14:26:19 +08:00
jakcron 672828839a Port MetaProcess to libtoolchain. 2021-10-11 22:12:23 +08:00
jakcron 1444dff37a Move MetaProcess 2021-10-11 20:20:14 +08:00
jakcron aeb87156b5 Port NsoProcess and NroProcess to libtoolchain. 2021-10-11 20:18:43 +08:00
jakcron 948c0e965f Misc typo 2021-10-10 17:23:06 +08:00
jakcron 2d5f95fbcf Port PkiCertProcess to libtoolchain. 2021-10-10 17:22:55 +08:00
jakcron e623fa1b39 Update dependencies. 2021-10-10 17:20:43 +08:00
jakcron d2f1bed0e8 Updated libnintendo-hac 2021-10-10 09:35:04 +08:00
jakcron 43883c1a17 Update VisualStudio project files and silence warnings. 2021-10-08 18:52:57 +08:00
jakcron e7dfa8ad44 Port GameCardProcess & use new ExtractJob system. User can now specify custom extract jobs with specific virtual paths. 2021-10-08 18:05:51 +08:00
jakcron 9d41a1c913 Update libnintendo-hac 2021-10-08 18:02:32 +08:00
jakcron 6d9510cde4 Add the ability to import initial_data_kek 2021-10-08 17:59:27 +08:00
jakcron a3996eaf0c Add ExtractJob 2021-10-08 17:56:40 +08:00
jakcron 7ede5a081b Moved GameCardProcess 2021-10-03 12:03:39 +08:00
jakcron ece2906cde Added input stream property checks. 2021-10-03 12:01:23 +08:00
jakcron 5670cb7e16 Fix bug in detecting corrupt RomFs. 2021-10-03 10:56:55 +08:00
jakcron 41fee26d1f Remove unneeded include. 2021-10-03 10:56:39 +08:00
jakcron f734f8b4c6 Move old RomfsProcess header to not_ported. 2021-10-03 10:47:54 +08:00
jakcron 35cb707487 Port RomfsProcess to libtoolchain. 2021-10-03 10:47:11 +08:00
jakcron d373132e08 Change behaviour in how keydata is shown. 2021-10-02 23:02:46 +08:00
jakcron 345eb897c6 Update libnintendo-hac 2021-10-02 22:57:50 +08:00
jakcron 649bfb1f11 Change PfsProcess to use FsProcess and VirtualFileSystem 2021-10-02 22:57:35 +08:00
jakcron 7a9ef48a38 Add FsProcess 2021-10-02 22:57:12 +08:00
jakcron 5f1e8b27de Port PfsProcess to libtoolchain 2021-10-02 22:53:21 +08:00
jakcron ab2686fd78 Move PfsProcess 2021-10-02 13:42:45 +08:00
jakcron f8493e6686 Support importing XCI cert sign key. 2021-10-02 12:15:32 +08:00
jakcron ac5f20804f Misc 2021-10-02 12:13:35 +08:00
jakcron 9d99c20842 Misc. 2021-10-02 12:11:37 +08:00
jakcron f9c43a0fb5 Misc 2021-10-02 12:10:23 +08:00
jakcron f66ed2de0e Automatically load rsa public keys if not user supplied. 2021-10-02 12:08:34 +08:00
jakcron 06e8454c01 Support dumping keys. 2021-10-02 11:14:50 +08:00
jakcron d92e32c515 Port RoMetadatProcess to libtoolchain. 2021-10-01 21:14:46 +08:00
jakcron d9b2b51ac1 Move RoMetadataProcess back 2021-10-01 21:01:29 +08:00
jakcron 651c8e090d Add connections for NacpProcess. 2021-10-01 20:48:44 +08:00
jakcron a4effb5b6a Misc 2021-10-01 20:46:47 +08:00
jakcron bf53ac4640 Port NacpProcess to libtoolchain. 2021-10-01 20:46:35 +08:00
jakcron d63356e012 Move NacpProcess back. 2021-10-01 20:42:34 +08:00
jakcron 0fbfddae1d Update libnintendo-es with new propertymask flags. 2021-10-01 17:40:46 +08:00
jakcron 09e6b8a857 Move some files around to test each process as it's ported. 2021-10-01 17:39:34 +08:00
jakcron 258173915c Port ElfSymbolParser to libtoolchain. 2021-09-30 19:42:15 +08:00
jakcron 0c6efad603 Port CnmtProcess to libtoolchain. 2021-09-30 19:41:57 +08:00
jakcron f86716b95b Update libnintendo-hac-hb 2021-09-30 19:41:32 +08:00
jakcron b51ce51b15 Port AssetHeader to libtoolchain. 2021-09-30 19:41:17 +08:00
jakcron 6a3bd0e562 Ported PkiValidator to libtoolchain. 2021-09-30 19:40:45 +08:00
jakcron 5c970a2986 Prepared for future ecc signature support. 2021-09-30 19:40:27 +08:00
jakcron 0f16231638 Add getTruncatedBytesString() to utils. 2021-09-29 18:28:49 +08:00
jakcron 0e39d48214 Update Visual Studio project files. 2021-09-29 13:00:01 +08:00
jakcron 60acda6615 Begin migration from libfnd to libtoolchain 2021-09-28 19:15:54 +08:00
jakcron 6bc4cd8a8c Update dependencies. 2021-09-26 10:32:52 +08:00
jakcron 2f362f35fe Add libfmt and update dependencies. 2021-09-25 10:45:58 +08:00
jakcron 80951a2fb2 Add libtoolchain 2021-09-25 10:21:45 +08:00
jakcron 24306d80ee Catch when NCA partitions fail to be processed due to apparent corruption. 2021-09-24 18:19:22 +08:00
76 changed files with 7062 additions and 5839 deletions

View file

@ -1,10 +1,10 @@
name: Compile Master Branch
name: Compile Project (Push/PR/Release)
on:
push:
branches: [ master ]
branches: [ development-tip, stable ]
pull_request:
branches: [ master ]
branches: [ development-tip, stable ]
release:
types: [ created ]
@ -14,45 +14,60 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
dist: [ubuntu_x86_64, macos_x86_64]
dist: [ubuntu_x86_64, macos_x86_64, macos_arm64]
prog: [nstool]
include:
- dist: ubuntu_x86_64
os: ubuntu-latest
arch: x86_64
bin_ext:
- dist: macos_x86_64
os: macos-latest
arch: x86_64
bin_ext:
- dist: macos_arm64
os: macos-latest
arch: arm64
bin_ext:
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4
- name: Clone submodules
run: git submodule init && git submodule update
- name: Compile ${{ matrix.prog }}
run: make deps && make
- uses: actions/upload-artifact@v2
run: make PROJECT_PLATFORM_ARCH=${{ matrix.arch }} deps all
- uses: actions/upload-artifact@v4
with:
name: ${{ matrix.prog }}-${{ matrix.dist }}
path: ./bin/${{ matrix.prog }}
path: ./bin/${{ matrix.prog }}${{ matrix.bin_ext }}
build_visualstudio:
name: Compile ${{ matrix.prog }} for ${{ matrix.dist }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
dist: [win_x86_64]
dist: [win_x64, win_x86]
prog: [nstool]
include:
- dist: win_x86_64
- dist: win_x64
os: windows-latest
platform: x64
configuration: Release
build_path: x64\Release
bin_ext: .exe
- dist: win_x86
os: windows-latest
platform: x86
configuration: Release
build_path: Release
bin_ext: .exe
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v1.0.0
uses: microsoft/setup-msbuild@v1.3
- name: Clone submodules
run: git submodule init && git submodule update
- name: Compile ${{ matrix.prog }}
run: msbuild .\build\visualstudio\${{ matrix.prog }}.sln /p:configuration=${{ matrix.configuration }} /p:platform=${{ matrix.platform }}
- uses: actions/upload-artifact@v2
- uses: actions/upload-artifact@v4
with:
name: ${{ matrix.prog }}-${{ matrix.dist }}
path: .\build\visualstudio\${{ matrix.platform }}\${{ matrix.configuration }}\${{ matrix.prog }}.exe
path: .\build\visualstudio\${{ matrix.build_path }}\${{ matrix.prog }}${{ matrix.bin_ext }}

30
.gitmodules vendored
View file

@ -1,21 +1,15 @@
[submodule "deps/libmbedtls"]
path = deps/libmbedtls
url = https://github.com/jakcron/libmbedtls.git
[submodule "deps/libfmt"]
path = deps/libfmt
url = https://github.com/jakcron/libfmt.git
[submodule "deps/libtoolchain"]
path = deps/libtoolchain
url = https://github.com/jakcron/libtoolchain.git
[submodule "deps/liblz4"]
path = deps/liblz4
url = https://github.com/jakcron/liblz4.git
[submodule "deps/libfnd"]
path = deps/libfnd
url = https://github.com/jakcron/libfnd.git
[submodule "deps/libnintendo-pki"]
path = deps/libnintendo-pki
url = https://github.com/jakcron/libnintendo-pki.git
[submodule "deps/libnintendo-es"]
path = deps/libnintendo-es
url = https://github.com/jakcron/libnintendo-es.git
[submodule "deps/libnintendo-hac"]
path = deps/libnintendo-hac
url = https://github.com/jakcron/libnintendo-hac.git
[submodule "deps/libnintendo-hac-hb"]
path = deps/libnintendo-hac-hb
url = https://github.com/jakcron/libnintendo-hac-hb.git
[submodule "deps/libmbedtls"]
path = deps/libmbedtls
url = https://github.com/jakcron/libmbedtls
[submodule "deps/libpietendo"]
path = deps/libpietendo
url = https://github.com/jakcron/libpietendo.git

View file

@ -1,4 +1,13 @@
# Building
## Git Submodules
This project makes use of git submodules to import dependencies into the source tree.
After cloning this repository using git, prior to building NSTool the dependencies need to be downloaded.
Run these two commands to initialise and download the dependencies:
```
git submodule init
git submodule update
```
## Linux (incl. Windows Subsystem for Linux) & MacOS - Makefile
### Requirements
* `make`
@ -12,9 +21,9 @@
* `make deps` - Compile locally included dependency libraries
* `make clean_deps` - Remove compiled library binaries and object files
## Native Win32 - Visual Studio
## Native Windows - Visual Studio
### Requirements
* [Visual Studio Community](https://visualstudio.microsoft.com/vs/community/) 2015 or 2017
* [Visual Studio Community](https://visualstudio.microsoft.com/vs/community/) 2015 / 2017 / 2019
### Compiling NSTool
* Open `build/visualstudio/nstool.sln` in Visual Studio

212
README.md
View file

@ -2,78 +2,162 @@
General purpose reading/extraction tool for Nintendo Switch file formats.
## Supported File Formats
* Meta (.npdm)
* PartitionFS (and HashedPartitionFS) (includes raw .nsp)
* RomFS (and CompressedRomFS)
* NX GameCard Image (.xci)
* PartitionFs (`PFS0`) (.pfs0)
* Sha256PartitionFs (`HFS0`) (.hfs0)
* RomFs (.romfs)
* Nintendo Content Archive (.nca)
* Content Metadata (.cnmt)
* Nintendo Software Object (.nso)
* Nintendo Relocatable Software Object (.nro)
* Kernel Initial Process List (.ini)
* Kernel Initial Process (.kip)
* Nintendo Submission Package (.nsp)
* NX GameCard Image (.xci)
* Meta (`META`) (.npdm)
* Nintendo Application Control Property (.nacp)
* Content Metadata (.cnmt)
* ES Certificate (.cert)
* ES Ticket (v2 only) (.tik)
* PKI Certificate (.cert)
* Nintendo Shared Object (`NSO0`) (.nso)
* Nintendo Relocatable Object (`NRO0`) (.nro)
* Initial Program Bundle (`INI1`) (.ini)
* Initial Program (`KIP1`) (.kip)
# Usage
## General usage
The default mode of NSTool is to show general information about a file.
To display general information the usage is as follows:
```
Usage: nstool [options... ] <file>
General Options:
-d, --dev Use devkit keyset.
-k, --keyset Specify keyset file.
-t, --type Specify input file type. [xci, pfs, romfs, nca, meta, cnmt, nso, nro, ini, kip, nacp, aset, cert, tik]
-y, --verify Verify file.
Output Options:
--showkeys Show keys generated.
--showlayout Show layout metadata.
-v, --verbose Verbose output.
XCI (GameCard Image)
nstool [--listfs] [--update <dir> --logo <dir> --normal <dir> --secure <dir>] <.xci file>
--listfs Print file system in embedded partitions.
--update Extract "update" partition to directory.
--logo Extract "logo" partition to directory.
--normal Extract "normal" partition to directory.
--secure Extract "secure" partition to directory.
PFS0/HFS0 (PartitionFs), RomFs, NSP (Ninendo Submission Package)
nstool [--listfs] [--fsdir <dir>] <file>
--listfs Print file system.
--fsdir Extract file system to directory.
NCA (Nintendo Content Archive)
nstool [--listfs] [--bodykey <key> --titlekey <key>] [--part0 <dir> ...] <.nca file>
--listfs Print file system in embedded partitions.
--titlekey Specify title key extracted from ticket.
--bodykey Specify body encryption key.
--tik Specify ticket to source title key.
--cert Specify certificate chain to verify ticket.
--part0 Extract "partition 0" to directory.
--part1 Extract "partition 1" to directory.
--part2 Extract "partition 2" to directory.
--part3 Extract "partition 3" to directory.
NSO (Nintendo Software Object), NRO (Nintendo Relocatable Object)
nstool [--listapi --listsym] [--insttype <inst. type>] <file>
--listapi Print SDK API List.
--listsym Print Code Symbols.
--insttype Specify instruction type [64bit|32bit] (64bit is assumed).
INI (Initial Process List Blob)
nstool [--kipdir <dir>] <file>
--kipdir Extract embedded KIPs to directory.
ASET (Homebrew Asset Blob)
nstool [--listfs] [--icon <file> --nacp <file> --fsdir <dir>] <file>
--listfs Print filesystem in embedded RomFS partition.
--icon Extract icon partition to file.
--nacp Extract NACP partition to file.
--fsdir Extract RomFS partition to directory.
nstool some_file.bin
```
However not all information is shown in this mode; file-layout, key data and properties set to default values are omitted.
## Alternative output modes
To output file-layout information, use the `--showlayout` option:
```
nstool --showlayout some_file.bin
```
To output key data generation and selection, use the `--showkeys` option:
```
nstool --showkeys some_file.bin
```
To output all information, enable the verbose output mode with the `-v` or `--verbose` option:
```
nstool -v some_file.bin
```
## Specify File Type
NSTool will in most cases correctly identify the file type. However you can override this and manually specify the file type with the `-t` or `--type` option:
```
nstool -t cnmt some_file.bin
```
In that example `cnmt` was selected, NSTool would process the file as `Content Metadata`. See below for a list of supported file type codes:
| Code | Description |
| ----------- | --------------- |
| gc, xci | NX GameCard Image |
| nsp | Nintendo Submission Package |
| pfs | PartitionFs |
| hfs | Sha256PartitionFs |
| romfs | RomFs |
| nca | Nintendo Content Archive |
| meta, npdm | Meta (.npdm) |
| cnmt | Content Metadata |
| nso | Nintendo Shared Object |
| nro | Nintendo Relocatable Object |
| ini | Initial Program Bundle |
| kip | Initial Program |
| nacp | Nintendo Application Control Property |
| cert | ES Certificate |
| tik | ES Ticket |
| aset, asset | Homebrew NRO Asset Binary |
## Validate Input File
Some file types have signatures/hashes/fields that can be validated by NSTool, but this mode isn't enabled by default.
To validate files with NSTool, enable the verify mode with the `-y` or `--verify` option:
```
nstool -y some_file.bin
```
See the below table for file types that support optional validation:
| File Type | Validation | Comments |
| --------- | ---------- | -------- |
| ES Certificate | Signature | If certificate is part of a certificate chain it will validate it as part of that chain. `Root` signed certificates are verified with user supplied `Root` public key. |
| ES Ticket | Signature | If the user specifies a certificate chain with `--cert` option, the ticket will be verified against that certificate chain. |
| NX GameCard Image | XCI Header Signature, HFS0 Hashes | XCI header signature is verified with user supplied `XCI Header` public key. |
| META | AccessControlInfo fields, AccessControlInfoDesc signature | AccessControlInfo fields are validated against the AccessControlInfoDesc. AccessControlInfoDesc signature is verfied with the appropriate user supplied `ACID` public key. |
| NCA | Header Signature[0], Header Signature[1] | Header Signature[0] is verified with the appropriate user supplied `NCA Header` public key. Header Signature[1] is verified only in Program titles, by retrieving the with public key from the AccessControlInfoDesc stored in the `code` partition. |
* As of Nintendo Switch Firmware 9.0.0, Nintendo retroactively added key generations for some public keys, including `NCA Header` and `ACID` public keys, so the various generations for these public keys will have to be supplied by the user.
* As of NSTool v1.6.0 the public key(s) for `Root Certificate`, `XCI Header`, `ACID` and `NCA Header` are built-in, and will be used if the user does not supply the public key in a key file.
## DevKit Mode
Files generated for `Production` use different (for the most part) encryption/signing keys than files generated for `Development`. NSTool will select `Production` encryption/signing keys by default.
When handling files intended for developer consoles (e.g. systemupdaters, devtools, test builds, etc), you should enable developer mode with the `-d`, `--dev` option:
```
nstool -d some_file.bin
```
## Extract Files
Some file types have an internal file system. This can be displayed and extracted.
To display the file system tree, use the file tree option `--fstree`:
```
nstool --fstree some_file.bin
```
To extract the file system, use the extract option `-x`, `--extract`. Which has four modes.
1) Extract the entire file system.
This extracts the contents of the entire file system to `./extract_dir/`. `extract_dir` will be created if it doesn't exist.
```
nstool -x ./extract_dir/ some_file.bin
```
2) Extract a sub directory.
This extracts the contents of `/a/sub/directory/` to `./extract_dir/`. `extract_dir` will be created if it doesn't exist.
```
nstool -x /a/sub/directory/ ./extract_dir/ some_file.bin
```
3) Extract a specific file, preserving the original name.
This extracts `/path/to/a/file.bin` to `./extract_dir/file.bin`.
```
nstool -x /path/to/a/file.bin ./extract_dir/ some_file.bin
```
4) Extract a specific file with a custom name.
This extracts `/path/to/a/file.bin` to `./extract_dir/different_name.bin`.
```
nstool -x /path/to/a/file.bin ./extract_dir/different_name.bin some_file.bin
```
### Supported File Types
* PartitionFs
* Sha256PartitionFs
* RomFs (including RomFs embedded in Homebrew NRO)
* NCA
* NSP
* XCI
## NCA Patches
Nintendo distributes game patches/updates in the style of a diff to keep file sizes down. This means extracting game patches requires the base version of the game to be able to process patch data. Typically this is only done for the Program NCA.
If `basegame_v0.nca` is the base Program NCA, and `gamepatch_v13219.nca` is the patch Program NCA, simply specify the base NCA using the base NCA option `--basenca` when processing the patch NCA.
```
nstool --basenca ./basegame_v0.nca -x ./patchdata gamepatch_v13219.nca
```
In the above example the patch NCA is being extracted to `./patchdata`
## Encrypted Files
Some Nintendo Switch files are partially or completely encrypted. These require the user to supply the encryption keys to NSTool so that it can process them.
See [SWITCH_KEYS.md](/SWITCH_KEYS.md) for more info.
# External Keys
NSTool doesn't embed any keys that are copyright protected. However keys can be imported via various keyset files.

View file

@ -8,25 +8,26 @@ If a keyset file is located in ___$HOME/.switch/___ it will be loaded automatica
# General Keys (prod.keys and dev.keys)
Some switch files formats feature encryption and or cryptographic signatures. In order to process these file formats, some keys are required. These keys can be supplied via a keyfile: ___prod.keys___ (or ___dev.keys___ for devkit variants).
This keyset file can be provided via the command line (refer to usage for details).
This keyset file can be provided via the command line, use the `-k` or `--keyset` option:
```
nstool -k prod.keys some_file.bin
```
## Format
The following keys are recognised (## represents a hexadecimal number between 00 and 1F):
The following keys are recognised (## represents the key revision, a hexadecimal number between 00 and FF):
```
; Key Sources
master_key_## : Master key, used to derive other keys. (0x10 bytes)
aes_kek_generation_source : Used to derive other aes-keks. (0x10 bytes)
aes_key_generation_source : Used to derive other aes-keys. (0x10 bytes)
package2_key_source : Used with master_key_## to derive package2_key_##. (0x10 bytes)
ticket_commonkey_source : Used with master_key_## to derive ticket_commonkey_##. (0x10 bytes)
nca_header_kek_source : Used with master_key_00, aes_kek_generation_source and aes_key_generation_source to generate nca_header_kek. (0x10 bytes)
nca_header_key_source : Used with nca_header_kek to generate nca_header_key. (0x20 bytes)
nca_body_keak_application_source : Used with master_key_##, aes_kek_generation_source and aes_key_generation_source to generate nca_body_keak_application_##. (0x10 bytes)
nca_body_keak_ocean_source : Used with master_key_##, aes_kek_generation_source and aes_key_generation_source to generate nca_body_keak_ocean_##. (0x10 bytes)
nca_body_keak_system_source : Used with master_key_##, aes_kek_generation_source and aes_key_generation_source to generate nca_body_keak_system_##. (0x10 bytes)
master_key_## : Master key, used to derive other keys. (0x10 bytes)
aes_kek_generation_source : Used to derive other aes-keks. (0x10 bytes)
aes_key_generation_source : Used to derive other aes-keys. (0x10 bytes)
package2_key_source : Used with master_key_## to derive package2_key_##. (0x10 bytes)
ticket_commonkey_source : Used with master_key_## to derive ticket_commonkey_##. (0x10 bytes)
nca_header_kek_source : Used with master_key_00, aes_kek_generation_source and aes_key_generation_source to generate nca_header_kek. (0x10 bytes)
nca_header_key_source : Used with nca_header_kek to generate nca_header_key. (0x20 bytes)
nca_key_area_key_application_source : Used with master_key_##, aes_kek_generation_source and aes_key_generation_source to generate nca_key_area_key_application_##. (0x10 bytes)
nca_key_area_key_ocean_source : Used with master_key_##, aes_kek_generation_source and aes_key_generation_source to generate nca_key_area_key_ocean_##. (0x10 bytes)
nca_key_area_key_system_source : Used with master_key_##, aes_kek_generation_source and aes_key_generation_source to generate nca_key_area_key_system_##. (0x10 bytes)
; Package1 keys
package1_key_## : AES128 Key (0x10 bytes)
@ -47,18 +48,24 @@ pki_root_sign_key_private : RSA4096 Private Exponent (0x200 bytes)
nca_header_key : AES128-XTS Key (0x20 bytes)
nca_header_sign_key_##_modulus : RSA2048 Modulus (0x100 bytes)
nca_header_sign_key_##_private : RSA2048 Private Exponent (0x100 bytes)
nca_body_keak_application_## : AES128 Key (0x10 bytes)
nca_body_keak_ocean_## : AES128 Key (0x10 bytes)
nca_body_keak_system_## : AES128 Key (0x10 bytes)
nca_key_area_key_application_## : AES128 Key (0x10 bytes)
nca_key_area_key_ocean_## : AES128 Key (0x10 bytes)
nca_key_area_key_system_## : AES128 Key (0x10 bytes)
nca_key_area_key_hw_application_## : AES128 Key (0x10 bytes)
nca_key_area_key_hw_ocean_## : AES128 Key (0x10 bytes)
nca_key_area_key_hw_system_## : AES128 Key (0x10 bytes)
; NRR Keys
nrr_certificate_sign_key_##_modulus : RSA2048 Modulus (0x100 bytes)
nrr_certificate_sign_key_##_private : RSA2048 Private Exponent (0x100 bytes)
; XCI Keys
xci_header_key : AES128 Key (0x10 bytes)
xci_header_key_## : AES128 Key (0x10 bytes)
xci_header_sign_key_modulus : RSA2048 Modulus (0x100 bytes)
xci_header_sign_key_private : RSA2048 Private Exponent (0x100 bytes)
xci_initial_data_kek_## : AES128 Key (0x10 bytes)
xci_cert_sign_key_modulus : RSA2048 Modulus (0x100 bytes)
xci_cert_sign_key_private : RSA2048 Private Exponent (0x100 bytes)
; ACID Keys
acid_sign_key_##_modulus : RSA2048 Modulus (0x100 bytes)
@ -80,12 +87,134 @@ acid_sign_key_private : alias for acid_sign_key_00_private
titlekek_source : hactool alias for ticket_commonkey_source
header_key_source : hactool alias for nca_header_key_source
header_kek_source : hactool alias for nca_header_kek_source
key_area_key_application_source : hactool alias for nca_body_keak_application_source
key_area_key_ocean_source : hactool alias for nca_body_keak_ocean_source
key_area_key_system_source : hactool alias for nca_body_keak_system_source
key_area_key_application_source : hactool alias for nca_key_area_key_application_source
key_area_key_ocean_source : hactool alias for nca_key_area_key_ocean_source
key_area_key_system_source : hactool alias for nca_key_area_key_system_source
titlekek_## : hactool alias for ticket_commonkey_##
header_key : hactool alias for nca_header_key
key_area_key_application_## : hactool alias for nca_body_keak_application_##
key_area_key_ocean_## : hactool alias for nca_body_keak_ocean_##
key_area_key_system_## : hactool alias for nca_body_keak_system_##
key_area_key_application_## : hactool alias for nca_key_area_key_application_##
key_area_key_ocean_## : hactool alias for nca_key_area_key_ocean_##
key_area_key_system_## : hactool alias for nca_key_area_key_system_##
```
## Encrypted File Types
See below for advice on what keys are required to decrypt certain file types.
### NX GameCard Image
The `GameCard ExtendedHeader` is encrypted with one of 8 keys, specified by the `KekIndex` in the `GameCard Header`.
It isn't required to extract game data, it just contains metadata.
Only two keys are currently defined:
| KeyIndex | Name | Description |
| ----------- | --------------- | ----------- |
| 00 | Production | Usually selected for prod images. Some dev images use this key index. |
| 01 | Development | Usually selected for dev images. This was changed from key index 00 at some point. |
Define the header key(s) in `prod.keys`/`dev.keys` (Prod and dev share the same keydata):
```
xci_header_key_00 = <32 char AES128 key here>
xci_header_key_01 = <32 char AES128 key here>
```
### Nintendo Content Archive
Nintendo Content Archive (NCA) files have both an encrypted header and content. The encrypted header determines the layout/format/encryption method of the content, which contains the game data.
Define the header key in `prod.keys`/`dev.keys`.
```
nca_header_key = <64 char AES128-XTS key-data here>
```
Or allow NSTool to derive it from key sources:
```
master_key_00 = <32 char AES128 key-data here>
aes_kek_generation_source = <32 char AES128 key-data here>
aes_key_generation_source = <32 char AES128 key-data here>
nca_header_kek_source = <32 char AES128 key-data here>
nca_header_key_source = <64 char AES128 key-data here>
```
In order to read the NCA content, the content key must be determined. Unlike the header key which is fixed, each NCA will have a unique content key.
Content keys are either:
1) "Internal", where they are encrypted the NCA Header KeyArea
2) "External", where they are encrypted in an external Ticket file (.tik) (external content keys are sometimes called title keys)
#### Internal Content Key
Decrypting the content key from the NCA Header Key Area requires the appropriate `nca_key_area_key` to be defined in `prod.keys`/`dev.keys`.
However for security reasons Nintendo revises this key periodically, and within each key revision there are 3 separate keys for different categories of applications.
It's best to define as many of these as possible, to reduce the number of times you need to edit the keyfiles.
So for a given key revision these key area keys can be defined explicitly (`##` represents the key revision in hexadecimal):
```
nca_key_area_key_application_## = <32 char AES128 key-data here>
nca_key_area_key_ocean_## = <32 char AES128 key-data here>
nca_key_area_key_system_## = <32 char AES128 key-data here>
```
Or allow NSTool to derive them from key sources: (`##` represents the key revision in hexadecimal):
```
master_key_## = <32 char AES128 key-data here>
aes_kek_generation_source = <32 char AES128 key-data here>
aes_key_generation_source = <32 char AES128 key-data here>
nca_key_area_key_application_source = <32 char AES128 key-data here>
nca_key_area_key_ocean_source = <32 char AES128 key-data here>
nca_key_area_key_system_source = <32 char AES128 key-data here>
```
#### External Content Key
For NCAs that use an external content key, the user must supplied the key to NSTool.
Most NCAs that use an external content key will be bundled with a ticket file (*.tik) that contains the content key in an encrypted form.
The ticket can be supplied by the user using the `--tik` option:
```
nstool --tik <32 char rightsid>.tik <32 char contentid>.nca
```
This however requires the the appropriate commonkey to be defined in `prod.keys`/`dev.keys` to decrypt the content key in the ticket. However for security reasons Nintendo revises this key periodically.
It's best to define as many of these as possible, to reduce the number of times you need to edit the keyfiles.
So for a given key revision the common key can be defined explicitly (`##` represents the key revision in hexadecimal):
```
ticket_commonkey_## = <32 char AES128 key-data here>
```
Or allow NSTool to derive them from key sources: (`##` represents the key revision in hexadecimal):
```
master_key_## = <32 char AES128 key-data here>
ticket_commonkey_source = <32 char AES128 key-data here>
```
##### Supply the external content key directly to NSTool
Alternatively you can supply the raw encrypted content key (also called a title key) directly with the `--titlekey` option:
```
nstool --titlekey <32 char AES128 key-data here> <32 char contentid>.nca
```
It is also possible to supply the decrypted content key directly with the `--contentkey` option:
```
nstool --contentkey <32 char AES128 key-data here> <32 char contentid>.nca
```
##### Scene Tickets
Please note that "Scene" tickets have been known to have errors. If you have issues using the `--tik` option, try passing the raw encrypted titlekey directly with the `--titlekey` option. The titlekey can be found by reading the ticket with NSTool:
```
nstool <32 char rightsid>.tik
```
##### Personalised Tickets
If the ticket is personalised (encrypted with console unique RSA key), NSTool will not support it. You will need to use extract the title key with another tool and pass the encrypted title key directly with the `--titlekey` option.
# Title Keys (title.keys)
In order for NSTool to decrypt NCA files that use external content keys, the ticket or key data be provided to NSTool. For convience NSTool supports the hactool `title.keys` format. This file can store a dictionary of title keys, so that specifying a ticket or key data manually is not required, provided it is present in `title.keys`. This file must be present in: ___$HOME/.switch/___ .
## Format
* This file is in the format of (rights_id = title_key) pairs, each on their own line.
* There is no limit on the number of pairs.
* The `;` is the comment indicator. When parsing a file, it is treated as a new line character.
* The format is case insensitive
### Example
For example if rights id `010003000e1468000000000000000008` had a title key `8fa820b219781d331cca08968e6e5b52`, the row would look like this:
```
010003000e1468000000000000000008 = 8fa820b219781d331cca08968e6e5b52
```

View file

@ -1,129 +1,90 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.28010.2036
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nstool", "nstool\nstool.vcxproj", "{775EF5EB-CA49-4994-8AC4-47B4A5385266}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "deps", "deps", "{05929EAE-4471-4E8E-A6F3-793A81623D7F}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libfnd", "..\..\deps\libfnd\build\visualstudio\libfnd\libfnd.vcxproj", "{4E578016-34BA-4A1E-B8EC-37A48780B6CA}"
ProjectSection(ProjectDependencies) = postProject
{E741ADED-7900-4E07-8DB0-D008C336C3FB} = {E741ADED-7900-4E07-8DB0-D008C336C3FB}
{7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C} = {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "liblz4", "..\..\deps\liblz4\build\visualstudio\liblz4\liblz4.vcxproj", "{E741ADED-7900-4E07-8DB0-D008C336C3FB}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libnintendo-es", "..\..\deps\libnintendo-es\build\visualstudio\libnintendo-es\libnintendo-es.vcxproj", "{8616D6C9-C8DE-4C3F-AFC2-625636664C2B}"
ProjectSection(ProjectDependencies) = postProject
{4E578016-34BA-4A1E-B8EC-37A48780B6CA} = {4E578016-34BA-4A1E-B8EC-37A48780B6CA}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libnintendo-hac", "..\..\deps\libnintendo-hac\build\visualstudio\libnintendo-hac\libnintendo-hac.vcxproj", "{8885C125-83FB-4F73-A93A-C712B1434D54}"
ProjectSection(ProjectDependencies) = postProject
{4E578016-34BA-4A1E-B8EC-37A48780B6CA} = {4E578016-34BA-4A1E-B8EC-37A48780B6CA}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libnintendo-hac-hb", "..\..\deps\libnintendo-hac-hb\build\visualstudio\libnintendo-hac-hb\libnintendo-hac-hb.vcxproj", "{24D001B4-D439-4967-9371-DC3E0523EB19}"
ProjectSection(ProjectDependencies) = postProject
{4E578016-34BA-4A1E-B8EC-37A48780B6CA} = {4E578016-34BA-4A1E-B8EC-37A48780B6CA}
{8885C125-83FB-4F73-A93A-C712B1434D54} = {8885C125-83FB-4F73-A93A-C712B1434D54}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libnintendo-pki", "..\..\deps\libnintendo-pki\build\visualstudio\libnintendo-pki\libnintendo-pki.vcxproj", "{0BEF63A0-2801-4563-AB65-1E2FD881C3AF}"
ProjectSection(ProjectDependencies) = postProject
{4E578016-34BA-4A1E-B8EC-37A48780B6CA} = {4E578016-34BA-4A1E-B8EC-37A48780B6CA}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmbedtls", "..\..\deps\libmbedtls\build\visualstudio\libmbedtls\libmbedtls.vcxproj", "{7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{775EF5EB-CA49-4994-8AC4-47B4A5385266}.Debug|x64.ActiveCfg = Debug|x64
{775EF5EB-CA49-4994-8AC4-47B4A5385266}.Debug|x64.Build.0 = Debug|x64
{775EF5EB-CA49-4994-8AC4-47B4A5385266}.Debug|x86.ActiveCfg = Debug|Win32
{775EF5EB-CA49-4994-8AC4-47B4A5385266}.Debug|x86.Build.0 = Debug|Win32
{775EF5EB-CA49-4994-8AC4-47B4A5385266}.Release|x64.ActiveCfg = Release|x64
{775EF5EB-CA49-4994-8AC4-47B4A5385266}.Release|x64.Build.0 = Release|x64
{775EF5EB-CA49-4994-8AC4-47B4A5385266}.Release|x86.ActiveCfg = Release|Win32
{775EF5EB-CA49-4994-8AC4-47B4A5385266}.Release|x86.Build.0 = Release|Win32
{4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Debug|x64.ActiveCfg = Debug|x64
{4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Debug|x64.Build.0 = Debug|x64
{4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Debug|x86.ActiveCfg = Debug|Win32
{4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Debug|x86.Build.0 = Debug|Win32
{4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Release|x64.ActiveCfg = Release|x64
{4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Release|x64.Build.0 = Release|x64
{4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Release|x86.ActiveCfg = Release|Win32
{4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Release|x86.Build.0 = Release|Win32
{E741ADED-7900-4E07-8DB0-D008C336C3FB}.Debug|x64.ActiveCfg = Debug|x64
{E741ADED-7900-4E07-8DB0-D008C336C3FB}.Debug|x64.Build.0 = Debug|x64
{E741ADED-7900-4E07-8DB0-D008C336C3FB}.Debug|x86.ActiveCfg = Debug|Win32
{E741ADED-7900-4E07-8DB0-D008C336C3FB}.Debug|x86.Build.0 = Debug|Win32
{E741ADED-7900-4E07-8DB0-D008C336C3FB}.Release|x64.ActiveCfg = Release|x64
{E741ADED-7900-4E07-8DB0-D008C336C3FB}.Release|x64.Build.0 = Release|x64
{E741ADED-7900-4E07-8DB0-D008C336C3FB}.Release|x86.ActiveCfg = Release|Win32
{E741ADED-7900-4E07-8DB0-D008C336C3FB}.Release|x86.Build.0 = Release|Win32
{8616D6C9-C8DE-4C3F-AFC2-625636664C2B}.Debug|x64.ActiveCfg = Debug|x64
{8616D6C9-C8DE-4C3F-AFC2-625636664C2B}.Debug|x64.Build.0 = Debug|x64
{8616D6C9-C8DE-4C3F-AFC2-625636664C2B}.Debug|x86.ActiveCfg = Debug|Win32
{8616D6C9-C8DE-4C3F-AFC2-625636664C2B}.Debug|x86.Build.0 = Debug|Win32
{8616D6C9-C8DE-4C3F-AFC2-625636664C2B}.Release|x64.ActiveCfg = Release|x64
{8616D6C9-C8DE-4C3F-AFC2-625636664C2B}.Release|x64.Build.0 = Release|x64
{8616D6C9-C8DE-4C3F-AFC2-625636664C2B}.Release|x86.ActiveCfg = Release|Win32
{8616D6C9-C8DE-4C3F-AFC2-625636664C2B}.Release|x86.Build.0 = Release|Win32
{8885C125-83FB-4F73-A93A-C712B1434D54}.Debug|x64.ActiveCfg = Debug|x64
{8885C125-83FB-4F73-A93A-C712B1434D54}.Debug|x64.Build.0 = Debug|x64
{8885C125-83FB-4F73-A93A-C712B1434D54}.Debug|x86.ActiveCfg = Debug|Win32
{8885C125-83FB-4F73-A93A-C712B1434D54}.Debug|x86.Build.0 = Debug|Win32
{8885C125-83FB-4F73-A93A-C712B1434D54}.Release|x64.ActiveCfg = Release|x64
{8885C125-83FB-4F73-A93A-C712B1434D54}.Release|x64.Build.0 = Release|x64
{8885C125-83FB-4F73-A93A-C712B1434D54}.Release|x86.ActiveCfg = Release|Win32
{8885C125-83FB-4F73-A93A-C712B1434D54}.Release|x86.Build.0 = Release|Win32
{24D001B4-D439-4967-9371-DC3E0523EB19}.Debug|x64.ActiveCfg = Debug|x64
{24D001B4-D439-4967-9371-DC3E0523EB19}.Debug|x64.Build.0 = Debug|x64
{24D001B4-D439-4967-9371-DC3E0523EB19}.Debug|x86.ActiveCfg = Debug|Win32
{24D001B4-D439-4967-9371-DC3E0523EB19}.Debug|x86.Build.0 = Debug|Win32
{24D001B4-D439-4967-9371-DC3E0523EB19}.Release|x64.ActiveCfg = Release|x64
{24D001B4-D439-4967-9371-DC3E0523EB19}.Release|x64.Build.0 = Release|x64
{24D001B4-D439-4967-9371-DC3E0523EB19}.Release|x86.ActiveCfg = Release|Win32
{24D001B4-D439-4967-9371-DC3E0523EB19}.Release|x86.Build.0 = Release|Win32
{0BEF63A0-2801-4563-AB65-1E2FD881C3AF}.Debug|x64.ActiveCfg = Debug|x64
{0BEF63A0-2801-4563-AB65-1E2FD881C3AF}.Debug|x64.Build.0 = Debug|x64
{0BEF63A0-2801-4563-AB65-1E2FD881C3AF}.Debug|x86.ActiveCfg = Debug|Win32
{0BEF63A0-2801-4563-AB65-1E2FD881C3AF}.Debug|x86.Build.0 = Debug|Win32
{0BEF63A0-2801-4563-AB65-1E2FD881C3AF}.Release|x64.ActiveCfg = Release|x64
{0BEF63A0-2801-4563-AB65-1E2FD881C3AF}.Release|x64.Build.0 = Release|x64
{0BEF63A0-2801-4563-AB65-1E2FD881C3AF}.Release|x86.ActiveCfg = Release|Win32
{0BEF63A0-2801-4563-AB65-1E2FD881C3AF}.Release|x86.Build.0 = Release|Win32
{7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Debug|x64.ActiveCfg = Debug|x64
{7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Debug|x64.Build.0 = Debug|x64
{7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Debug|x86.ActiveCfg = Debug|Win32
{7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Debug|x86.Build.0 = Debug|Win32
{7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Release|x64.ActiveCfg = Release|x64
{7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Release|x64.Build.0 = Release|x64
{7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Release|x86.ActiveCfg = Release|Win32
{7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{4E578016-34BA-4A1E-B8EC-37A48780B6CA} = {05929EAE-4471-4E8E-A6F3-793A81623D7F}
{E741ADED-7900-4E07-8DB0-D008C336C3FB} = {05929EAE-4471-4E8E-A6F3-793A81623D7F}
{8616D6C9-C8DE-4C3F-AFC2-625636664C2B} = {05929EAE-4471-4E8E-A6F3-793A81623D7F}
{8885C125-83FB-4F73-A93A-C712B1434D54} = {05929EAE-4471-4E8E-A6F3-793A81623D7F}
{24D001B4-D439-4967-9371-DC3E0523EB19} = {05929EAE-4471-4E8E-A6F3-793A81623D7F}
{0BEF63A0-2801-4563-AB65-1E2FD881C3AF} = {05929EAE-4471-4E8E-A6F3-793A81623D7F}
{7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C} = {05929EAE-4471-4E8E-A6F3-793A81623D7F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {ABDCFB40-D6B3-44A9-92B5-0D7AB38D9FB8}
EndGlobalSection
EndGlobal

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.2.32616.157
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nstool", "nstool\nstool.vcxproj", "{775EF5EB-CA49-4994-8AC4-47B4A5385266}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "deps", "deps", "{05929EAE-4471-4E8E-A6F3-793A81623D7F}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "liblz4", "..\..\deps\liblz4\build\visualstudio\liblz4\liblz4.vcxproj", "{E741ADED-7900-4E07-8DB0-D008C336C3FB}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmbedtls", "..\..\deps\libmbedtls\build\visualstudio\libmbedtls\libmbedtls.vcxproj", "{7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libtoolchain", "..\..\deps\libtoolchain\build\visualstudio\libtoolchain\libtoolchain.vcxproj", "{E194E4B8-1482-40A2-901B-75D4387822E9}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libfmt", "..\..\deps\libfmt\build\visualstudio\libfmt\libfmt.vcxproj", "{F4B0540E-0AAE-4006-944B-356944EF61FA}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libpietendo", "..\..\deps\libpietendo\build\visualstudio\libpietendo\libpietendo.vcxproj", "{5ADDD009-9D25-40BE-B2A6-2F3AB4DCBBD2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{775EF5EB-CA49-4994-8AC4-47B4A5385266}.Debug|x64.ActiveCfg = Debug|x64
{775EF5EB-CA49-4994-8AC4-47B4A5385266}.Debug|x64.Build.0 = Debug|x64
{775EF5EB-CA49-4994-8AC4-47B4A5385266}.Debug|x86.ActiveCfg = Debug|Win32
{775EF5EB-CA49-4994-8AC4-47B4A5385266}.Debug|x86.Build.0 = Debug|Win32
{775EF5EB-CA49-4994-8AC4-47B4A5385266}.Release|x64.ActiveCfg = Release|x64
{775EF5EB-CA49-4994-8AC4-47B4A5385266}.Release|x64.Build.0 = Release|x64
{775EF5EB-CA49-4994-8AC4-47B4A5385266}.Release|x86.ActiveCfg = Release|Win32
{775EF5EB-CA49-4994-8AC4-47B4A5385266}.Release|x86.Build.0 = Release|Win32
{E741ADED-7900-4E07-8DB0-D008C336C3FB}.Debug|x64.ActiveCfg = Debug|x64
{E741ADED-7900-4E07-8DB0-D008C336C3FB}.Debug|x64.Build.0 = Debug|x64
{E741ADED-7900-4E07-8DB0-D008C336C3FB}.Debug|x86.ActiveCfg = Debug|Win32
{E741ADED-7900-4E07-8DB0-D008C336C3FB}.Debug|x86.Build.0 = Debug|Win32
{E741ADED-7900-4E07-8DB0-D008C336C3FB}.Release|x64.ActiveCfg = Release|x64
{E741ADED-7900-4E07-8DB0-D008C336C3FB}.Release|x64.Build.0 = Release|x64
{E741ADED-7900-4E07-8DB0-D008C336C3FB}.Release|x86.ActiveCfg = Release|Win32
{E741ADED-7900-4E07-8DB0-D008C336C3FB}.Release|x86.Build.0 = Release|Win32
{7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Debug|x64.ActiveCfg = Debug|x64
{7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Debug|x64.Build.0 = Debug|x64
{7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Debug|x86.ActiveCfg = Debug|Win32
{7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Debug|x86.Build.0 = Debug|Win32
{7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Release|x64.ActiveCfg = Release|x64
{7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Release|x64.Build.0 = Release|x64
{7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Release|x86.ActiveCfg = Release|Win32
{7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Release|x86.Build.0 = Release|Win32
{E194E4B8-1482-40A2-901B-75D4387822E9}.Debug|x64.ActiveCfg = Debug|x64
{E194E4B8-1482-40A2-901B-75D4387822E9}.Debug|x64.Build.0 = Debug|x64
{E194E4B8-1482-40A2-901B-75D4387822E9}.Debug|x86.ActiveCfg = Debug|Win32
{E194E4B8-1482-40A2-901B-75D4387822E9}.Debug|x86.Build.0 = Debug|Win32
{E194E4B8-1482-40A2-901B-75D4387822E9}.Release|x64.ActiveCfg = Release|x64
{E194E4B8-1482-40A2-901B-75D4387822E9}.Release|x64.Build.0 = Release|x64
{E194E4B8-1482-40A2-901B-75D4387822E9}.Release|x86.ActiveCfg = Release|Win32
{E194E4B8-1482-40A2-901B-75D4387822E9}.Release|x86.Build.0 = Release|Win32
{F4B0540E-0AAE-4006-944B-356944EF61FA}.Debug|x64.ActiveCfg = Debug|x64
{F4B0540E-0AAE-4006-944B-356944EF61FA}.Debug|x64.Build.0 = Debug|x64
{F4B0540E-0AAE-4006-944B-356944EF61FA}.Debug|x86.ActiveCfg = Debug|Win32
{F4B0540E-0AAE-4006-944B-356944EF61FA}.Debug|x86.Build.0 = Debug|Win32
{F4B0540E-0AAE-4006-944B-356944EF61FA}.Release|x64.ActiveCfg = Release|x64
{F4B0540E-0AAE-4006-944B-356944EF61FA}.Release|x64.Build.0 = Release|x64
{F4B0540E-0AAE-4006-944B-356944EF61FA}.Release|x86.ActiveCfg = Release|Win32
{F4B0540E-0AAE-4006-944B-356944EF61FA}.Release|x86.Build.0 = Release|Win32
{5ADDD009-9D25-40BE-B2A6-2F3AB4DCBBD2}.Debug|x64.ActiveCfg = Debug|x64
{5ADDD009-9D25-40BE-B2A6-2F3AB4DCBBD2}.Debug|x64.Build.0 = Debug|x64
{5ADDD009-9D25-40BE-B2A6-2F3AB4DCBBD2}.Debug|x86.ActiveCfg = Debug|Win32
{5ADDD009-9D25-40BE-B2A6-2F3AB4DCBBD2}.Debug|x86.Build.0 = Debug|Win32
{5ADDD009-9D25-40BE-B2A6-2F3AB4DCBBD2}.Release|x64.ActiveCfg = Release|x64
{5ADDD009-9D25-40BE-B2A6-2F3AB4DCBBD2}.Release|x64.Build.0 = Release|x64
{5ADDD009-9D25-40BE-B2A6-2F3AB4DCBBD2}.Release|x86.ActiveCfg = Release|Win32
{5ADDD009-9D25-40BE-B2A6-2F3AB4DCBBD2}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{E741ADED-7900-4E07-8DB0-D008C336C3FB} = {05929EAE-4471-4E8E-A6F3-793A81623D7F}
{7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C} = {05929EAE-4471-4E8E-A6F3-793A81623D7F}
{E194E4B8-1482-40A2-901B-75D4387822E9} = {05929EAE-4471-4E8E-A6F3-793A81623D7F}
{F4B0540E-0AAE-4006-944B-356944EF61FA} = {05929EAE-4471-4E8E-A6F3-793A81623D7F}
{5ADDD009-9D25-40BE-B2A6-2F3AB4DCBBD2} = {05929EAE-4471-4E8E-A6F3-793A81623D7F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {ABDCFB40-D6B3-44A9-92B5-0D7AB38D9FB8}
EndGlobalSection
EndGlobal

View file

@ -1,196 +1,197 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{775EF5EB-CA49-4994-8AC4-47B4A5385266}</ProjectGuid>
<RootNamespace>nstool</RootNamespace>
<WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\deps\libfnd\build\visualstudio\libfnd\libfnd.vcxproj">
<Project>{4e578016-34ba-4a1e-b8ec-37a48780b6ca}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\deps\liblz4\build\visualstudio\liblz4\liblz4.vcxproj">
<Project>{e741aded-7900-4e07-8db0-d008c336c3fb}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\deps\libnintendo-es\build\visualstudio\libnintendo-es\libnintendo-es.vcxproj">
<Project>{8616d6c9-c8de-4c3f-afc2-625636664c2b}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\deps\libnintendo-hac-hb\build\visualstudio\libnintendo-hac-hb\libnintendo-hac-hb.vcxproj">
<Project>{24d001b4-d439-4967-9371-dc3e0523eb19}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\deps\libnintendo-hac\build\visualstudio\libnintendo-hac\libnintendo-hac.vcxproj">
<Project>{8885c125-83fb-4f73-a93a-c712b1434d54}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\deps\libnintendo-pki\build\visualstudio\libnintendo-pki\libnintendo-pki.vcxproj">
<Project>{0bef63a0-2801-4563-ab65-1e2fd881c3af}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\deps\libmbedtls\build\visualstudio\libmbedtls\libmbedtls.vcxproj">
<Project>{7a7c66f3-2b5b-4e23-85d8-2a74fedad92c}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\..\src\AssetProcess.cpp" />
<ClCompile Include="..\..\..\src\CnmtProcess.cpp" />
<ClCompile Include="..\..\..\src\CompressedArchiveIFile.cpp" />
<ClCompile Include="..\..\..\src\ElfSymbolParser.cpp" />
<ClCompile Include="..\..\..\src\EsTikProcess.cpp" />
<ClCompile Include="..\..\..\src\GameCardProcess.cpp" />
<ClCompile Include="..\..\..\src\IniProcess.cpp" />
<ClCompile Include="..\..\..\src\KeyConfiguration.cpp" />
<ClCompile Include="..\..\..\src\KipProcess.cpp" />
<ClCompile Include="..\..\..\src\main.cpp" />
<ClCompile Include="..\..\..\src\MetaProcess.cpp" />
<ClCompile Include="..\..\..\src\NacpProcess.cpp" />
<ClCompile Include="..\..\..\src\NcaProcess.cpp" />
<ClCompile Include="..\..\..\src\NroProcess.cpp" />
<ClCompile Include="..\..\..\src\NsoProcess.cpp" />
<ClCompile Include="..\..\..\src\PfsProcess.cpp" />
<ClCompile Include="..\..\..\src\PkiCertProcess.cpp" />
<ClCompile Include="..\..\..\src\PkiValidator.cpp" />
<ClCompile Include="..\..\..\src\RoMetadataProcess.cpp" />
<ClCompile Include="..\..\..\src\RomfsProcess.cpp" />
<ClCompile Include="..\..\..\src\SdkApiString.cpp" />
<ClCompile Include="..\..\..\src\UserSettings.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\src\AssetProcess.h" />
<ClInclude Include="..\..\..\src\CnmtProcess.h" />
<ClInclude Include="..\..\..\src\common.h" />
<ClInclude Include="..\..\..\src\CompressedArchiveIFile.h" />
<ClInclude Include="..\..\..\src\ElfSymbolParser.h" />
<ClInclude Include="..\..\..\src\EsTikProcess.h" />
<ClInclude Include="..\..\..\src\GameCardProcess.h" />
<ClInclude Include="..\..\..\src\IniProcess.h" />
<ClInclude Include="..\..\..\src\KeyConfiguration.h" />
<ClInclude Include="..\..\..\src\KipProcess.h" />
<ClInclude Include="..\..\..\src\MetaProcess.h" />
<ClInclude Include="..\..\..\src\NacpProcess.h" />
<ClInclude Include="..\..\..\src\NcaProcess.h" />
<ClInclude Include="..\..\..\src\NroProcess.h" />
<ClInclude Include="..\..\..\src\NsoProcess.h" />
<ClInclude Include="..\..\..\src\PfsProcess.h" />
<ClInclude Include="..\..\..\src\PkiCertProcess.h" />
<ClInclude Include="..\..\..\src\PkiValidator.h" />
<ClInclude Include="..\..\..\src\RoMetadataProcess.h" />
<ClInclude Include="..\..\..\src\RomfsProcess.h" />
<ClInclude Include="..\..\..\src\SdkApiString.h" />
<ClInclude Include="..\..\..\src\UserSettings.h" />
<ClInclude Include="..\..\..\src\version.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{775EF5EB-CA49-4994-8AC4-47B4A5385266}</ProjectGuid>
<RootNamespace>nstool</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\liblz4\include;$(SolutionDir)..\..\deps\libtoolchain\include;$(SolutionDir)..\..\deps\libfmt\include;$(SolutionDir)..\..\deps\libpietendo\include</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\liblz4\include;$(SolutionDir)..\..\deps\libtoolchain\include;$(SolutionDir)..\..\deps\libfmt\include;$(SolutionDir)..\..\deps\libpietendo\include</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\liblz4\include;$(SolutionDir)..\..\deps\libtoolchain\include;$(SolutionDir)..\..\deps\libfmt\include;$(SolutionDir)..\..\deps\libpietendo\include</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>$(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\liblz4\include;$(SolutionDir)..\..\deps\libtoolchain\include;$(SolutionDir)..\..\deps\libfmt\include;$(SolutionDir)..\..\deps\libpietendo\include</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ProjectReference Include="$(SolutionDir)..\..\deps\libfmt\build\visualstudio\libfmt\libfmt.vcxproj">
<Project>{f4b0540e-0aae-4006-944b-356944ef61fa}</Project>
</ProjectReference>
<ProjectReference Include="$(SolutionDir)..\..\deps\liblz4\build\visualstudio\liblz4\liblz4.vcxproj">
<Project>{e741aded-7900-4e07-8db0-d008c336c3fb}</Project>
</ProjectReference>
<ProjectReference Include="$(SolutionDir)..\..\deps\libmbedtls\build\visualstudio\libmbedtls\libmbedtls.vcxproj">
<Project>{7a7c66f3-2b5b-4e23-85d8-2a74fedad92c}</Project>
</ProjectReference>
<ProjectReference Include="$(SolutionDir)..\..\deps\libtoolchain\build\visualstudio\libtoolchain\libtoolchain.vcxproj">
<Project>{e194e4b8-1482-40a2-901b-75d4387822e9}</Project>
</ProjectReference>
<ProjectReference Include="$(SolutionDir)..\..\deps\libpietendo\build\visualstudio\libpietendo\libpietendo.vcxproj">
<Project>{5addd009-9d25-40be-b2a6-2f3ab4dcbbd2}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\src\AssetProcess.h" />
<ClInclude Include="..\..\..\src\CnmtProcess.h" />
<ClInclude Include="..\..\..\src\elf.h" />
<ClInclude Include="..\..\..\src\ElfSymbolParser.h" />
<ClInclude Include="..\..\..\src\EsCertProcess.h" />
<ClInclude Include="..\..\..\src\EsTikProcess.h" />
<ClInclude Include="..\..\..\src\FsProcess.h" />
<ClInclude Include="..\..\..\src\GameCardProcess.h" />
<ClInclude Include="..\..\..\src\IniProcess.h" />
<ClInclude Include="..\..\..\src\KeyBag.h" />
<ClInclude Include="..\..\..\src\KipProcess.h" />
<ClInclude Include="..\..\..\src\MetaProcess.h" />
<ClInclude Include="..\..\..\src\NacpProcess.h" />
<ClInclude Include="..\..\..\src\NcaProcess.h" />
<ClInclude Include="..\..\..\src\NroProcess.h" />
<ClInclude Include="..\..\..\src\NsoProcess.h" />
<ClInclude Include="..\..\..\src\PfsProcess.h" />
<ClInclude Include="..\..\..\src\PkiValidator.h" />
<ClInclude Include="..\..\..\src\RoMetadataProcess.h" />
<ClInclude Include="..\..\..\src\RomfsProcess.h" />
<ClInclude Include="..\..\..\src\SdkApiString.h" />
<ClInclude Include="..\..\..\src\Settings.h" />
<ClInclude Include="..\..\..\src\types.h" />
<ClInclude Include="..\..\..\src\util.h" />
<ClInclude Include="..\..\..\src\version.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\..\src\AssetProcess.cpp" />
<ClCompile Include="..\..\..\src\CnmtProcess.cpp" />
<ClCompile Include="..\..\..\src\ElfSymbolParser.cpp" />
<ClCompile Include="..\..\..\src\EsCertProcess.cpp" />
<ClCompile Include="..\..\..\src\EsTikProcess.cpp" />
<ClCompile Include="..\..\..\src\FsProcess.cpp" />
<ClCompile Include="..\..\..\src\GameCardProcess.cpp" />
<ClCompile Include="..\..\..\src\IniProcess.cpp" />
<ClCompile Include="..\..\..\src\KeyBag.cpp" />
<ClCompile Include="..\..\..\src\KipProcess.cpp" />
<ClCompile Include="..\..\..\src\main.cpp" />
<ClCompile Include="..\..\..\src\MetaProcess.cpp" />
<ClCompile Include="..\..\..\src\NacpProcess.cpp" />
<ClCompile Include="..\..\..\src\NcaProcess.cpp" />
<ClCompile Include="..\..\..\src\NroProcess.cpp" />
<ClCompile Include="..\..\..\src\NsoProcess.cpp" />
<ClCompile Include="..\..\..\src\PfsProcess.cpp" />
<ClCompile Include="..\..\..\src\PkiValidator.cpp" />
<ClCompile Include="..\..\..\src\RoMetadataProcess.cpp" />
<ClCompile Include="..\..\..\src\RomfsProcess.cpp" />
<ClCompile Include="..\..\..\src\SdkApiString.cpp" />
<ClCompile Include="..\..\..\src\Settings.cpp" />
<ClCompile Include="..\..\..\src\util.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -1,156 +1,165 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\..\src\AssetProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\CnmtProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\CompressedArchiveIFile.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\ElfSymbolParser.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\EsTikProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\GameCardProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\IniProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\KeyConfiguration.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\KipProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\MetaProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\NacpProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\NcaProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\NroProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\NsoProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\PfsProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\PkiCertProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\PkiValidator.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\RoMetadataProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\RomfsProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\SdkApiString.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\UserSettings.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\src\AssetProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\CnmtProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\common.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\CompressedArchiveIFile.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\ElfSymbolParser.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\EsTikProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\GameCardProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\IniProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\KeyConfiguration.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\KipProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\MetaProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\NacpProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\NcaProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\NroProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\NsoProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\PfsProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\PkiCertProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\PkiValidator.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\RoMetadataProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\RomfsProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\SdkApiString.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\UserSettings.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\version.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\src\AssetProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\CnmtProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\elf.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\ElfSymbolParser.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\EsCertProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\EsTikProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\FsProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\GameCardProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\IniProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\KeyBag.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\KipProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\MetaProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\NacpProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\NcaProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\NroProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\NsoProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\PfsProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\PkiValidator.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\RoMetadataProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\RomfsProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\SdkApiString.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\Settings.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\types.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\util.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\version.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\..\src\AssetProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\CnmtProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\ElfSymbolParser.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\EsCertProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\EsTikProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\FsProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\GameCardProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\IniProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\KeyBag.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\KipProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\MetaProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\NacpProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\NcaProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\NsoProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\NroProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\PfsProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\PkiValidator.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\RoMetadataProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\RomfsProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\SdkApiString.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\Settings.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\util.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

1
deps/libfmt vendored Submodule

@ -0,0 +1 @@
Subproject commit b03b081e026ceb1226b45de926babe629d6c0688

1
deps/libfnd vendored

@ -1 +0,0 @@
Subproject commit 19c1683060a8b39b737da8505b5e23660ed86282

2
deps/liblz4 vendored

@ -1 +1 @@
Subproject commit 0e5a8c29295a9046fff4ad5371a8ea682c7e0cb3
Subproject commit 561775a11e00cbd21a45031c64068cb5e1242608

2
deps/libmbedtls vendored

@ -1 +1 @@
Subproject commit bc43e5e079529455749d81d1a3b77a9574d5ab01
Subproject commit 74346376dd035e9fbc161cdb80afc646b72fde86

1
deps/libnintendo-es vendored

@ -1 +0,0 @@
Subproject commit 9e3f1ea763be033f60b3b2db0b2d6e2aac462a37

@ -1 +0,0 @@
Subproject commit afbbe3900d4c0dab6b3c4cd06927aff227cc1f95

@ -1 +0,0 @@
Subproject commit 95fb4d7762eb5b395fa5023fd3a9b6f34151505a

@ -1 +0,0 @@
Subproject commit 5097871222f6e2cd07b7b8e8b58551e913eb1c15

1
deps/libpietendo vendored Submodule

@ -0,0 +1 @@
Subproject commit 5d50d02f8f3b8918bdfbbb599b8c4c1628660d89

1
deps/libtoolchain vendored Submodule

@ -0,0 +1 @@
Subproject commit 578d170f5b294e4a9feb3cc2d504896e846f204e

View file

@ -1,6 +1,6 @@
# C++/C Recursive Project Makefile
# (c) Jack
# Version 3
# Version 9 (20231231)
# Project Name
PROJECT_NAME = nstool
@ -13,7 +13,7 @@ PROJECT_SRC_SUBDIRS = $(PROJECT_SRC_PATH)
#PROJECT_TESTSRC_PATH = test
#PROJECT_TESTSRC_SUBDIRS = $(PROJECT_TESTSRC_PATH)
PROJECT_BIN_PATH = bin
#PROJECT_DOCS_PATH = docs
#PROJECT_DOCS_PATH = docs
#PROJECT_DOXYFILE_PATH = Doxyfile
# Determine if the root makefile has been established, and if not establish this makefile as the root makefile
@ -23,16 +23,9 @@ ifeq ($(ROOT_PROJECT_NAME),)
export ROOT_PROJECT_DEPENDENCY_PATH = $(ROOT_PROJECT_PATH)/deps
endif
# Shared Library Definitions
PROJECT_SO_VER_MAJOR = 0
PROJECT_SO_VER_MINOR = 1
PROJECT_SO_VER_PATCH = 0
PROJECT_SONAME = $(PROJECT_NAME).so.$(PROJECT_SO_VER_MAJOR)
PROJECT_SO_FILENAME = $(PROJECT_SONAME).$(PROJECT_SO_VER_MINOR).$(PROJECT_SO_VER_PATCH)
# Project Dependencies
PROJECT_DEPEND_LOCAL = nintendo-hac-hb nintendo-hac nintendo-es nintendo-pki fnd mbedtls lz4
PROJECT_DEPEND_EXTERNAL =
PROJECT_DEPEND = pietendo toolchain fmt lz4 mbedtls
PROJECT_DEPEND_LOCAL_DIR = libpietendo libtoolchain libfmt liblz4 libmbedtls
# Generate compiler flags for including project include path
ifneq ($(PROJECT_INCLUDE_PATH),)
@ -40,14 +33,14 @@ ifneq ($(PROJECT_INCLUDE_PATH),)
endif
# Generate compiler flags for local included dependencies
ifneq ($(PROJECT_DEPEND_LOCAL),)
LIB += $(foreach dep,$(PROJECT_DEPEND_LOCAL), -L"$(ROOT_PROJECT_DEPENDENCY_PATH)/lib$(dep)/bin" -l$(dep))
INC += $(foreach dep,$(PROJECT_DEPEND_LOCAL), -I"$(ROOT_PROJECT_DEPENDENCY_PATH)/lib$(dep)/include")
ifneq ($(PROJECT_DEPEND_LOCAL_DIR),)
LIB += $(foreach dep,$(PROJECT_DEPEND_LOCAL_DIR), -L"$(ROOT_PROJECT_DEPENDENCY_PATH)/$(dep)/bin")
INC += $(foreach dep,$(PROJECT_DEPEND_LOCAL_DIR), -I"$(ROOT_PROJECT_DEPENDENCY_PATH)/$(dep)/include")
endif
# Generate compiler flags for external dependencies
ifneq ($(PROJECT_DEPEND_EXTERNAL),)
LIB += $(foreach dep,$(PROJECT_DEPEND_EXTERNAL), -l$(dep))
ifneq ($(PROJECT_DEPEND),)
LIB += $(foreach dep,$(PROJECT_DEPEND), -l$(dep))
endif
# Detect Platform
@ -64,47 +57,65 @@ ifeq ($(PROJECT_PLATFORM),)
endif
endif
# Detect Architecture
ifeq ($(PROJECT_PLATFORM_ARCH),)
ifeq ($(PROJECT_PLATFORM), WIN32)
export PROJECT_PLATFORM_ARCH = x86_64
else ifeq ($(PROJECT_PLATFORM), GNU)
export PROJECT_PLATFORM_ARCH = $(shell uname -m)
else ifeq ($(PROJECT_PLATFORM), MACOS)
export PROJECT_PLATFORM_ARCH = $(shell uname -m)
else
export PROJECT_PLATFORM_ARCH = x86_64
endif
endif
# Generate platform specific compiler flags
ifeq ($(PROJECT_PLATFORM), WIN32)
# Windows Flags/Libs
CC = x86_64-w64-mingw32-gcc
CXX = x86_64-w64-mingw32-g++
DEFINEFLAGS =
WARNFLAGS = -Wall -Wno-unused-value -Wno-unused-but-set-variable
ARCHFLAGS =
INC +=
LIB += -static
ARFLAGS = cr -o
ARFLAGS = cr
else ifeq ($(PROJECT_PLATFORM), GNU)
# GNU/Linux Flags/Libs
#CC =
#CXX =
DEFINEFLAGS =
WARNFLAGS = -Wall -Wno-unused-value -Wno-unused-but-set-variable
ARCHFLAGS =
INC +=
LIB +=
ARFLAGS = cr -o
ARFLAGS = cr
else ifeq ($(PROJECT_PLATFORM), MACOS)
# MacOS Flags/Libs
#CC =
#CXX =
DEFINEFLAGS =
WARNFLAGS = -Wall -Wno-unused-value -Wno-unused-private-field
ARCHFLAGS = -arch $(PROJECT_PLATFORM_ARCH)
INC +=
LIB +=
ARFLAGS = rc
ARFLAGS = rc
endif
# Compiler Flags
CXXFLAGS = -std=c++11 $(INC) $(WARNFLAGS) -fPIC
CFLAGS = -std=c11 $(INC) $(WARNFLAGS) -fPIC
CXXFLAGS = -std=c++11 $(INC) $(DEFINEFLAGS) $(WARNFLAGS) $(ARCHFLAGS) -fPIC
CFLAGS = -std=c11 $(INC) $(DEFINEFLAGS) $(WARNFLAGS) $(ARCHFLAGS) -fPIC
# Object Files
SRC_OBJ = $(foreach dir,$(PROJECT_SRC_SUBDIRS),$(subst .cpp,.o,$(wildcard $(dir)/*.cpp))) $(foreach dir,$(PROJECT_SRC_SUBDIRS),$(subst .c,.o,$(wildcard $(dir)/*.c)))
TESTSRC_OBJ = $(foreach dir,$(PROJECT_TESTSRC_SUBDIRS),$(subst .cpp,.o,$(wildcard $(dir)/*.cpp))) $(foreach dir,$(PROJECT_TESTSRC_SUBDIRS),$(subst .c,.o,$(wildcard $(dir)/*.c)))
SRC_OBJ = $(foreach dir,$(PROJECT_SRC_SUBDIRS),$(subst .cpp,.o,$(wildcard $(dir)/*.cpp))) $(foreach dir,$(PROJECT_SRC_SUBDIRS),$(subst .cc,.o,$(wildcard $(dir)/*.cc))) $(foreach dir,$(PROJECT_SRC_SUBDIRS),$(subst .c,.o,$(wildcard $(dir)/*.c)))
TESTSRC_OBJ = $(foreach dir,$(PROJECT_TESTSRC_SUBDIRS),$(subst .cpp,.o,$(wildcard $(dir)/*.cpp))) $(foreach dir,$(PROJECT_TESTSRC_SUBDIRS),$(subst .cc,.o,$(wildcard $(dir)/*.cc))) $(foreach dir,$(PROJECT_TESTSRC_SUBDIRS),$(subst .c,.o,$(wildcard $(dir)/*.c)))
# all is the default, user should specify what the default should do
# - 'static_lib' for building static library
# - 'shared_lib' for building shared library
# - 'program' for building the program
# - 'static_lib' for building source as a static library
# - 'program' for building source as executable program
# - 'test_program' for building the test program
# These can typically be used together however *_lib and program should not be used together
# test_program can be used with program or static_lib, but program and static_lib cannot be used together
all: program
clean: clean_object_files remove_binary_dir
@ -118,6 +129,10 @@ clean: clean_object_files remove_binary_dir
@echo CXX $<
@$(CXX) $(CXXFLAGS) -c $< -o $@
%.o: %.cc
@echo CXX $<
@$(CXX) $(CXXFLAGS) -c $< -o $@
# Binary Directory
.PHONY: create_binary_dir
create_binary_dir:
@ -138,20 +153,16 @@ static_lib: $(SRC_OBJ) create_binary_dir
@echo LINK $(PROJECT_BIN_PATH)/$(PROJECT_NAME).a
@ar $(ARFLAGS) "$(PROJECT_BIN_PATH)/$(PROJECT_NAME).a" $(SRC_OBJ)
shared_lib: $(SRC_OBJ) create_binary_dir
@echo LINK $(PROJECT_BIN_PATH)/$(PROJECT_SO_FILENAME)
@gcc -shared -Wl,-soname,$(PROJECT_SONAME) -o "$(PROJECT_BIN_PATH)/$(PROJECT_SO_FILENAME)" $(SRC_OBJ)
# Build Program
program: $(SRC_OBJ) create_binary_dir
@echo LINK $(PROJECT_BIN_PATH)/$(PROJECT_NAME)
@$(CXX) $(SRC_OBJ) $(LIB) -o "$(PROJECT_BIN_PATH)/$(PROJECT_NAME)"
@$(CXX) $(ARCHFLAGS) $(SRC_OBJ) $(LIB) -o "$(PROJECT_BIN_PATH)/$(PROJECT_NAME)"
# Build Test Program
test_program: $(TESTSRC_OBJ) $(SRC_OBJ) create_binary_dir
ifneq ($(PROJECT_TESTSRC_PATH),)
@echo LINK $(PROJECT_BIN_PATH)/$(PROJECT_NAME)_test
@$(CXX) $(TESTSRC_OBJ) $(SRC_OBJ) $(LIB) -o "$(PROJECT_BIN_PATH)/$(PROJECT_NAME)_test"
@$(CXX) $(ARCHFLAGS) $(TESTSRC_OBJ) $(SRC_OBJ) $(LIB) -o "$(PROJECT_BIN_PATH)/$(PROJECT_NAME)_test"
endif
# Documentation
@ -170,8 +181,8 @@ endif
# Dependencies
.PHONY: deps
deps:
@$(foreach lib,$(PROJECT_DEPEND_LOCAL), cd "$(ROOT_PROJECT_DEPENDENCY_PATH)/lib$(lib)" && $(MAKE) static_lib && cd "$(PROJECT_PATH)";)
@$(foreach lib,$(PROJECT_DEPEND_LOCAL_DIR), cd "$(ROOT_PROJECT_DEPENDENCY_PATH)/$(lib)" && $(MAKE) static_lib && cd "$(PROJECT_PATH)";)
.PHONY: clean_deps
clean_deps:
@$(foreach lib,$(PROJECT_DEPEND_LOCAL), cd "$(ROOT_PROJECT_DEPENDENCY_PATH)/lib$(lib)" && $(MAKE) clean && cd "$(PROJECT_PATH)";)
@$(foreach lib,$(PROJECT_DEPEND_LOCAL_DIR), cd "$(ROOT_PROJECT_DEPENDENCY_PATH)/$(lib)" && $(MAKE) clean && cd "$(PROJECT_PATH)";)

View file

@ -1,115 +1,106 @@
#include <iostream>
#include <iomanip>
#include <fnd/SimpleFile.h>
#include <fnd/OffsetAdjustedIFile.h>
#include <fnd/Vec.h>
#include "AssetProcess.h"
#include "util.h"
AssetProcess::AssetProcess() :
nstool::AssetProcess::AssetProcess() :
mModuleName("nstool::AssetProcess"),
mFile(),
mCliOutputMode(_BIT(OUTPUT_BASIC)),
mCliOutputMode(true, false, false, false),
mVerify(false)
{
}
}
void AssetProcess::process()
void nstool::AssetProcess::process()
{
importHeader();
if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
if (mCliOutputMode.show_basic_info)
displayHeader();
processSections();
}
}
void AssetProcess::setInputFile(const fnd::SharedPtr<fnd::IFile>& file)
void nstool::AssetProcess::setInputFile(const std::shared_ptr<tc::io::IStream>& file)
{
mFile = file;
}
void AssetProcess::setCliOutputMode(CliOutputMode type)
void nstool::AssetProcess::setCliOutputMode(CliOutputMode type)
{
mCliOutputMode = type;
}
void AssetProcess::setVerifyMode(bool verify)
void nstool::AssetProcess::setVerifyMode(bool verify)
{
mVerify = verify;
}
void AssetProcess::setListFs(bool list)
{
mRomfs.setListFs(list);
}
void AssetProcess::setIconExtractPath(const std::string& path)
void nstool::AssetProcess::setIconExtractPath(const tc::io::Path& path)
{
mIconExtractPath = path;
}
void AssetProcess::setNacpExtractPath(const std::string& path)
void nstool::AssetProcess::setNacpExtractPath(const tc::io::Path& path)
{
mNacpExtractPath = path;
}
void AssetProcess::setRomfsExtractPath(const std::string& path)
void nstool::AssetProcess::setRomfsShowFsTree(bool show_fs_tree)
{
mRomfs.setExtractPath(path);
mRomfs.setShowFsTree(show_fs_tree);
}
void AssetProcess::importHeader()
void nstool::AssetProcess::setRomfsExtractJobs(const std::vector<nstool::ExtractJob>& extract_jobs)
{
fnd::Vec<byte_t> scratch;
mRomfs.setExtractJobs(extract_jobs);
}
if (*mFile == nullptr)
void nstool::AssetProcess::importHeader()
{
if (mFile == nullptr)
{
throw fnd::Exception(kModuleName, "No file reader set.");
throw tc::Exception(mModuleName, "No file reader set.");
}
if (mFile->canRead() == false || mFile->canSeek() == false)
{
throw tc::NotSupportedException(mModuleName, "Input stream requires read/seek permissions.");
}
if ((*mFile)->size() < sizeof(nn::hac::sAssetHeader))
if (mFile->length() < tc::io::IOUtil::castSizeToInt64(sizeof(pie::hac::sAssetHeader)))
{
throw fnd::Exception(kModuleName, "Corrupt ASET: file too small");
throw tc::Exception(mModuleName, "Corrupt ASET: file too small");
}
scratch.alloc(sizeof(nn::hac::sAssetHeader));
(*mFile)->read(scratch.data(), 0, scratch.size());
tc::ByteData scratch = tc::ByteData(sizeof(pie::hac::sAssetHeader));
mFile->seek(0, tc::io::SeekOrigin::Begin);
mFile->read(scratch.data(), scratch.size());
mHdr.fromBytes(scratch.data(), scratch.size());
}
void AssetProcess::processSections()
void nstool::AssetProcess::processSections()
{
if (mHdr.getIconInfo().size > 0 && mIconExtractPath.isSet)
int64_t file_size = mFile->length();
if (mHdr.getIconInfo().size > 0 && mIconExtractPath.isSet())
{
if ((mHdr.getIconInfo().size + mHdr.getIconInfo().offset) > (*mFile)->size())
throw fnd::Exception(kModuleName, "ASET geometry for icon beyond file size");
if ((mHdr.getIconInfo().size + mHdr.getIconInfo().offset) > file_size)
throw tc::Exception(mModuleName, "ASET geometry for icon beyond file size");
fnd::SimpleFile outfile(mIconExtractPath.var, fnd::SimpleFile::Create);
fnd::Vec<byte_t> cache;
cache.alloc(mHdr.getIconInfo().size);
(*mFile)->read(cache.data(), mHdr.getIconInfo().offset, cache.size());
outfile.write(cache.data(), cache.size());
outfile.close();
fmt::print("Saving {:s}...", mIconExtractPath.get().to_string());
writeSubStreamToFile(mFile, mHdr.getIconInfo().offset, mHdr.getIconInfo().size, mIconExtractPath.get());
}
if (mHdr.getNacpInfo().size > 0)
{
if ((mHdr.getNacpInfo().size + mHdr.getNacpInfo().offset) > (*mFile)->size())
throw fnd::Exception(kModuleName, "ASET geometry for nacp beyond file size");
if ((mHdr.getNacpInfo().size + mHdr.getNacpInfo().offset) > file_size)
throw tc::Exception(mModuleName, "ASET geometry for nacp beyond file size");
if (mNacpExtractPath.isSet)
if (mNacpExtractPath.isSet())
{
fnd::SimpleFile outfile(mNacpExtractPath.var, fnd::SimpleFile::Create);
fnd::Vec<byte_t> cache;
cache.alloc(mHdr.getNacpInfo().size);
(*mFile)->read(cache.data(), mHdr.getNacpInfo().offset, cache.size());
outfile.write(cache.data(), cache.size());
outfile.close();
fmt::print("Saving {:s}...", mNacpExtractPath.get().to_string());
writeSubStreamToFile(mFile, mHdr.getNacpInfo().offset, mHdr.getNacpInfo().size, mNacpExtractPath.get());
}
mNacp.setInputFile(new fnd::OffsetAdjustedIFile(mFile, mHdr.getNacpInfo().offset, mHdr.getNacpInfo().size));
mNacp.setInputFile(std::make_shared<tc::io::SubStream>(mFile, mHdr.getNacpInfo().offset, mHdr.getNacpInfo().size));
mNacp.setCliOutputMode(mCliOutputMode);
mNacp.setVerifyMode(mVerify);
@ -118,10 +109,10 @@ void AssetProcess::processSections()
if (mHdr.getRomfsInfo().size > 0)
{
if ((mHdr.getRomfsInfo().size + mHdr.getRomfsInfo().offset) > (*mFile)->size())
throw fnd::Exception(kModuleName, "ASET geometry for romfs beyond file size");
if ((mHdr.getRomfsInfo().size + mHdr.getRomfsInfo().offset) > file_size)
throw tc::Exception(mModuleName, "ASET geometry for romfs beyond file size");
mRomfs.setInputFile(new fnd::OffsetAdjustedIFile(mFile, mHdr.getRomfsInfo().offset, mHdr.getRomfsInfo().size));
mRomfs.setInputFile(std::make_shared<tc::io::SubStream>(mFile, mHdr.getRomfsInfo().offset, mHdr.getRomfsInfo().size));
mRomfs.setCliOutputMode(mCliOutputMode);
mRomfs.setVerifyMode(mVerify);
@ -129,20 +120,20 @@ void AssetProcess::processSections()
}
}
void AssetProcess::displayHeader()
void nstool::AssetProcess::displayHeader()
{
if (_HAS_BIT(mCliOutputMode, OUTPUT_LAYOUT))
if (mCliOutputMode.show_layout)
{
std::cout << "[ASET Header]" << std::endl;
std::cout << " Icon:" << std::endl;
std::cout << " Offset: 0x" << std::hex << mHdr.getIconInfo().offset << std::endl;
std::cout << " Size: 0x" << std::hex << mHdr.getIconInfo().size << std::endl;
std::cout << " NACP:" << std::endl;
std::cout << " Offset: 0x" << std::hex << mHdr.getNacpInfo().offset << std::endl;
std::cout << " Size: 0x" << std::hex << mHdr.getNacpInfo().size << std::endl;
std::cout << " RomFS:" << std::endl;
std::cout << " Offset: 0x" << std::hex << mHdr.getRomfsInfo().offset << std::endl;
std::cout << " Size: 0x" << std::hex << mHdr.getRomfsInfo().size << std::endl;
fmt::print("[ASET Header]\n");
fmt::print(" Icon:\n");
fmt::print(" Offset: 0x{:x}\n", mHdr.getIconInfo().offset);
fmt::print(" Size: 0x{:x}\n", mHdr.getIconInfo().size);
fmt::print(" NACP:\n");
fmt::print(" Offset: 0x{:x}\n", mHdr.getNacpInfo().offset);
fmt::print(" Size: 0x{:x}\n", mHdr.getNacpInfo().size);
fmt::print(" RomFs:\n");
fmt::print(" Offset: 0x{:x}\n", mHdr.getRomfsInfo().offset);
fmt::print(" Size: 0x{:x}\n", mHdr.getRomfsInfo().size);
}
}

View file

@ -1,13 +1,11 @@
#pragma once
#include <string>
#include <fnd/types.h>
#include <fnd/IFile.h>
#include <fnd/SharedPtr.h>
#include <nn/hac/AssetHeader.h>
#include "types.h"
#include "NacpProcess.h"
#include "RomfsProcess.h"
#include "common.h"
#include <pietendo/hac/AssetHeader.h>
namespace nstool {
class AssetProcess
{
@ -16,32 +14,32 @@ public:
void process();
void setInputFile(const fnd::SharedPtr<fnd::IFile>& file);
void setInputFile(const std::shared_ptr<tc::io::IStream>& file);
void setCliOutputMode(CliOutputMode type);
void setVerifyMode(bool verify);
void setListFs(bool list);
void setIconExtractPath(const std::string& path);
void setNacpExtractPath(const std::string& path);
void setRomfsExtractPath(const std::string& path);
void setIconExtractPath(const tc::io::Path& path);
void setNacpExtractPath(const tc::io::Path& path);
void setRomfsShowFsTree(bool show_fs_tree);
void setRomfsExtractJobs(const std::vector<nstool::ExtractJob>& extract_jobs);
private:
const std::string kModuleName = "AssetProcess";
std::string mModuleName;
fnd::SharedPtr<fnd::IFile> mFile;
std::shared_ptr<tc::io::IStream> mFile;
CliOutputMode mCliOutputMode;
bool mVerify;
sOptional<std::string> mIconExtractPath;
sOptional<std::string> mNacpExtractPath;
tc::Optional<tc::io::Path> mIconExtractPath;
tc::Optional<tc::io::Path> mNacpExtractPath;
nn::hac::AssetHeader mHdr;
pie::hac::AssetHeader mHdr;
NacpProcess mNacp;
RomfsProcess mRomfs;
void importHeader();
void processSections();
void displayHeader();
};
};
}

View file

@ -1,222 +1,221 @@
#include "CnmtProcess.h"
#include <iostream>
#include <iomanip>
#include <pietendo/hac/ContentMetaUtil.h>
#include <fnd/SimpleTextOutput.h>
#include <fnd/OffsetAdjustedIFile.h>
#include <nn/hac/ContentMetaUtil.h>
CnmtProcess::CnmtProcess() :
nstool::CnmtProcess::CnmtProcess() :
mModuleName("nstool::CnmtProcess"),
mFile(),
mCliOutputMode(_BIT(OUTPUT_BASIC)),
mCliOutputMode(true, false, false, false),
mVerify(false)
{
}
void CnmtProcess::process()
void nstool::CnmtProcess::process()
{
importCnmt();
if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
if (mCliOutputMode.show_basic_info)
displayCnmt();
}
void CnmtProcess::setInputFile(const fnd::SharedPtr<fnd::IFile>& file)
void nstool::CnmtProcess::setInputFile(const std::shared_ptr<tc::io::IStream>& file)
{
mFile = file;
}
void CnmtProcess::setCliOutputMode(CliOutputMode type)
void nstool::CnmtProcess::setCliOutputMode(CliOutputMode type)
{
mCliOutputMode = type;
}
void CnmtProcess::setVerifyMode(bool verify)
void nstool::CnmtProcess::setVerifyMode(bool verify)
{
mVerify = verify;
}
const nn::hac::ContentMeta& CnmtProcess::getContentMeta() const
const pie::hac::ContentMeta& nstool::CnmtProcess::getContentMeta() const
{
return mCnmt;
}
void CnmtProcess::importCnmt()
void nstool::CnmtProcess::importCnmt()
{
fnd::Vec<byte_t> scratch;
if (*mFile == nullptr)
if (mFile == nullptr)
{
throw fnd::Exception(kModuleName, "No file reader set.");
throw tc::Exception(mModuleName, "No file reader set.");
}
if (mFile->canRead() == false || mFile->canSeek() == false)
{
throw tc::NotSupportedException(mModuleName, "Input stream requires read/seek permissions.");
}
// check if file_size is greater than 20MB, don't import.
size_t cnmt_file_size = tc::io::IOUtil::castInt64ToSize(mFile->length());
if (cnmt_file_size > (0x100000 * 20))
{
throw tc::Exception(mModuleName, "File too large.");
}
scratch.alloc((*mFile)->size());
(*mFile)->read(scratch.data(), 0, scratch.size());
// read cnmt
tc::ByteData scratch = tc::ByteData(cnmt_file_size);
mFile->seek(0, tc::io::SeekOrigin::Begin);
mFile->read(scratch.data(), scratch.size());
// parse cnmt
mCnmt.fromBytes(scratch.data(), scratch.size());
}
void CnmtProcess::displayCnmt()
void nstool::CnmtProcess::displayCnmt()
{
std::cout << "[ContentMeta]" << std::endl;
std::cout << " TitleId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mCnmt.getTitleId() << std::endl;
std::cout << " Version: " << nn::hac::ContentMetaUtil::getVersionAsString(mCnmt.getTitleVersion()) << " (v" << std::dec << mCnmt.getTitleVersion() << ")"<< std::endl;
std::cout << " Type: " << nn::hac::ContentMetaUtil::getContentMetaTypeAsString(mCnmt.getContentMetaType()) << " (" << std::dec << (uint32_t)mCnmt.getContentMetaType() << ")" << std::endl;
std::cout << " Attributes: 0x" << std::hex << mCnmt.getAttribute().to_ullong();
if (mCnmt.getAttribute().any())
const pie::hac::sContentMetaHeader* cnmt_hdr = (const pie::hac::sContentMetaHeader*)mCnmt.getBytes().data();
fmt::print("[ContentMeta]\n");
fmt::print(" TitleId: 0x{:016x}\n", mCnmt.getTitleId());
fmt::print(" Version: {:s} (v{:d})\n", pie::hac::ContentMetaUtil::getVersionAsString(mCnmt.getTitleVersion()), mCnmt.getTitleVersion());
fmt::print(" Type: {:s} ({:d})\n", pie::hac::ContentMetaUtil::getContentMetaTypeAsString(mCnmt.getContentMetaType()), (uint32_t)mCnmt.getContentMetaType());
fmt::print(" Attributes: 0x{:x}", *((byte_t*)&cnmt_hdr->attributes));
if (mCnmt.getAttribute().size())
{
std::vector<std::string> attribute_list;
for (size_t flag = 0; flag < mCnmt.getAttribute().size(); flag++)
for (auto itr = mCnmt.getAttribute().begin(); itr != mCnmt.getAttribute().end(); itr++)
{
if (mCnmt.getAttribute().test(flag))
{
attribute_list.push_back(nn::hac::ContentMetaUtil::getContentMetaAttributeFlagAsString(nn::hac::cnmt::ContentMetaAttributeFlag(flag)));
}
attribute_list.push_back(pie::hac::ContentMetaUtil::getContentMetaAttributeFlagAsString(pie::hac::cnmt::ContentMetaAttributeFlag(*itr)));
}
std::cout << " [";
fmt::print(" [");
for (auto itr = attribute_list.begin(); itr != attribute_list.end(); itr++)
{
std::cout << *itr;
fmt::print("{:s}",*itr);
if ((itr + 1) != attribute_list.end())
{
std::cout << ", ";
fmt::print(", ");
}
}
std::cout << "]";
fmt::print("]");
}
std::cout << std::endl;
fmt::print("\n");
std::cout << " StorageId: " << nn::hac::ContentMetaUtil::getStorageIdAsString(mCnmt.getStorageId()) << " (" << std::dec << (uint32_t)mCnmt.getStorageId() << ")" << std::endl;
std::cout << " ContentInstallType: " << nn::hac::ContentMetaUtil::getContentInstallTypeAsString(mCnmt.getContentInstallType()) << " (" << std::dec << (uint32_t)mCnmt.getContentInstallType() << ")" << std::endl;
std::cout << " RequiredDownloadSystemVersion: " << nn::hac::ContentMetaUtil::getVersionAsString(mCnmt.getRequiredDownloadSystemVersion()) << " (v" << mCnmt.getRequiredDownloadSystemVersion() << ")"<< std::endl;
fmt::print(" StorageId: {:s} ({:d})\n", pie::hac::ContentMetaUtil::getStorageIdAsString(mCnmt.getStorageId()), (uint32_t)mCnmt.getStorageId());
fmt::print(" ContentInstallType: {:s} ({:d})\n", pie::hac::ContentMetaUtil::getContentInstallTypeAsString(mCnmt.getContentInstallType()),(uint32_t)mCnmt.getContentInstallType());
fmt::print(" RequiredDownloadSystemVersion: {:s} (v{:d})\n", pie::hac::ContentMetaUtil::getVersionAsString(mCnmt.getRequiredDownloadSystemVersion()), mCnmt.getRequiredDownloadSystemVersion());
switch(mCnmt.getContentMetaType())
{
case (nn::hac::cnmt::ContentMetaType::Application):
std::cout << " ApplicationExtendedHeader:" << std::endl;
std::cout << " RequiredApplicationVersion: " << nn::hac::ContentMetaUtil::getVersionAsString(mCnmt.getApplicationMetaExtendedHeader().getRequiredApplicationVersion()) << " (v" << std::dec << mCnmt.getApplicationMetaExtendedHeader().getRequiredApplicationVersion() << ")"<< std::endl;
std::cout << " RequiredSystemVersion: " << nn::hac::ContentMetaUtil::getVersionAsString(mCnmt.getApplicationMetaExtendedHeader().getRequiredSystemVersion()) << " (v" << std::dec << mCnmt.getApplicationMetaExtendedHeader().getRequiredSystemVersion() << ")"<< std::endl;
std::cout << " PatchId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mCnmt.getApplicationMetaExtendedHeader().getPatchId() << std::endl;
case (pie::hac::cnmt::ContentMetaType_Application):
fmt::print(" ApplicationExtendedHeader:\n");
fmt::print(" RequiredApplicationVersion: {:s} (v{:d})\n", pie::hac::ContentMetaUtil::getVersionAsString(mCnmt.getApplicationMetaExtendedHeader().getRequiredApplicationVersion()), mCnmt.getApplicationMetaExtendedHeader().getRequiredApplicationVersion());
fmt::print(" RequiredSystemVersion: {:s} (v{:d})\n", pie::hac::ContentMetaUtil::getVersionAsString(mCnmt.getApplicationMetaExtendedHeader().getRequiredSystemVersion()), mCnmt.getApplicationMetaExtendedHeader().getRequiredSystemVersion());
fmt::print(" PatchId: 0x{:016x}\n", mCnmt.getApplicationMetaExtendedHeader().getPatchId());
break;
case (nn::hac::cnmt::ContentMetaType::Patch):
std::cout << " PatchMetaExtendedHeader:" << std::endl;
std::cout << " RequiredSystemVersion: " << nn::hac::ContentMetaUtil::getVersionAsString(mCnmt.getPatchMetaExtendedHeader().getRequiredSystemVersion()) << " (v" << std::dec << mCnmt.getPatchMetaExtendedHeader().getRequiredSystemVersion() << ")"<< std::endl;
std::cout << " ApplicationId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mCnmt.getPatchMetaExtendedHeader().getApplicationId() << std::endl;
case (pie::hac::cnmt::ContentMetaType_Patch):
fmt::print(" PatchMetaExtendedHeader:\n");
fmt::print(" RequiredSystemVersion: {:s} (v{:d})\n", pie::hac::ContentMetaUtil::getVersionAsString(mCnmt.getPatchMetaExtendedHeader().getRequiredSystemVersion()), mCnmt.getPatchMetaExtendedHeader().getRequiredSystemVersion());
fmt::print(" ApplicationId: 0x{:016x}\n", mCnmt.getPatchMetaExtendedHeader().getApplicationId());
break;
case (nn::hac::cnmt::ContentMetaType::AddOnContent):
std::cout << " AddOnContentMetaExtendedHeader:" << std::endl;
std::cout << " RequiredApplicationVersion: " << nn::hac::ContentMetaUtil::getVersionAsString(mCnmt.getAddOnContentMetaExtendedHeader().getRequiredApplicationVersion()) << " (v" << std::dec << mCnmt.getAddOnContentMetaExtendedHeader().getRequiredApplicationVersion() << ")" << std::endl;
std::cout << " ApplicationId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mCnmt.getAddOnContentMetaExtendedHeader().getApplicationId() << std::endl;
case (pie::hac::cnmt::ContentMetaType_AddOnContent):
fmt::print(" AddOnContentMetaExtendedHeader:\n");
fmt::print(" RequiredApplicationVersion: {:s} (v{:d})\n", pie::hac::ContentMetaUtil::getVersionAsString(mCnmt.getAddOnContentMetaExtendedHeader().getRequiredApplicationVersion()), mCnmt.getAddOnContentMetaExtendedHeader().getRequiredApplicationVersion());
fmt::print(" ApplicationId: 0x{:016x}\n", mCnmt.getAddOnContentMetaExtendedHeader().getApplicationId());
break;
case (nn::hac::cnmt::ContentMetaType::Delta):
std::cout << " DeltaMetaExtendedHeader:" << std::endl;
std::cout << " ApplicationId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mCnmt.getDeltaMetaExtendedHeader().getApplicationId() << std::endl;
case (pie::hac::cnmt::ContentMetaType_Delta):
fmt::print(" DeltaMetaExtendedHeader:\n");
fmt::print(" ApplicationId: 0x{:016x}\n", mCnmt.getDeltaMetaExtendedHeader().getApplicationId());
break;
default:
break;
}
if (mCnmt.getContentInfo().size() > 0)
{
printf(" ContentInfo:\n");
fmt::print(" ContentInfo:\n");
for (size_t i = 0; i < mCnmt.getContentInfo().size(); i++)
{
const nn::hac::ContentInfo& info = mCnmt.getContentInfo()[i];
std::cout << " " << std::dec << i << std::endl;
std::cout << " Type: " << nn::hac::ContentMetaUtil::getContentTypeAsString(info.getContentType()) << " (" << std::dec << (uint32_t)info.getContentType() << ")" << std::endl;
std::cout << " Id: " << fnd::SimpleTextOutput::arrayToString(info.getContentId().data(), info.getContentId().size(), false, "") << std::endl;
std::cout << " Size: 0x" << std::hex << info.getContentSize() << std::endl;
std::cout << " Hash: " << fnd::SimpleTextOutput::arrayToString(info.getContentHash().bytes, sizeof(info.getContentHash()), false, "") << std::endl;
const pie::hac::ContentInfo& info = mCnmt.getContentInfo()[i];
fmt::print(" {:d}\n", i);
fmt::print(" Type: {:s} ({:d})\n", pie::hac::ContentMetaUtil::getContentTypeAsString(info.getContentType()), (uint32_t)info.getContentType());
fmt::print(" Id: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(info.getContentId().data(), info.getContentId().size(), false, ""));
fmt::print(" Size: 0x{:x}\n", info.getContentSize());
fmt::print(" Hash: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(info.getContentHash().data(), info.getContentHash().size(), false, ""));
}
}
if (mCnmt.getContentMetaInfo().size() > 0)
{
std::cout << " ContentMetaInfo:" << std::endl;
fmt::print(" ContentMetaInfo:\n");
displayContentMetaInfoList(mCnmt.getContentMetaInfo(), " ");
}
// print extended data
if (mCnmt.getContentMetaType() == nn::hac::cnmt::ContentMetaType::Patch && mCnmt.getPatchMetaExtendedHeader().getExtendedDataSize() != 0)
if (mCnmt.getContentMetaType() == pie::hac::cnmt::ContentMetaType_Patch && mCnmt.getPatchMetaExtendedHeader().getExtendedDataSize() != 0)
{
// this is stubbed as the raw output is for development purposes
//std::cout << " PatchMetaExtendedData:" << std::endl;
//fnd::SimpleTextOutput::hxdStyleDump(mCnmt.getPatchMetaExtendedData().data(), mCnmt.getPatchMetaExtendedData().size());
//fmt::print(" PatchMetaExtendedData:\n");
//tc::cli::FormatUtil::formatBytesAsHxdHexString(mCnmt.getPatchMetaExtendedData().data(), mCnmt.getPatchMetaExtendedData().size());
}
else if (mCnmt.getContentMetaType() == nn::hac::cnmt::ContentMetaType::Delta && mCnmt.getDeltaMetaExtendedHeader().getExtendedDataSize() != 0)
else if (mCnmt.getContentMetaType() == pie::hac::cnmt::ContentMetaType_Delta && mCnmt.getDeltaMetaExtendedHeader().getExtendedDataSize() != 0)
{
// this is stubbed as the raw output is for development purposes
//std::cout << " DeltaMetaExtendedData:" << std::endl;
//fnd::SimpleTextOutput::hxdStyleDump(mCnmt.getDeltaMetaExtendedData().data(), mCnmt.getDeltaMetaExtendedData().size());
//fmt::print(" DeltaMetaExtendedData:\n");
//tc::cli::FormatUtil::formatBytesAsHxdHexString(mCnmt.getDeltaMetaExtendedData().data(), mCnmt.getDeltaMetaExtendedData().size());
}
else if (mCnmt.getContentMetaType() == nn::hac::cnmt::ContentMetaType::SystemUpdate && mCnmt.getSystemUpdateMetaExtendedHeader().getExtendedDataSize() != 0)
else if (mCnmt.getContentMetaType() == pie::hac::cnmt::ContentMetaType_SystemUpdate && mCnmt.getSystemUpdateMetaExtendedHeader().getExtendedDataSize() != 0)
{
std::cout << " SystemUpdateMetaExtendedData:" << std::endl;
std::cout << " FormatVersion: " << std::dec << mCnmt.getSystemUpdateMetaExtendedData().getFormatVersion() << std::endl;
std::cout << " FirmwareVariation:" << std::endl;
fmt::print(" SystemUpdateMetaExtendedData:\n");
fmt::print(" FormatVersion: {:d}\n", mCnmt.getSystemUpdateMetaExtendedData().getFormatVersion());
fmt::print(" FirmwareVariation:\n");
auto variation_info = mCnmt.getSystemUpdateMetaExtendedData().getFirmwareVariationInfo();
for (size_t i = 0; i < mCnmt.getSystemUpdateMetaExtendedData().getFirmwareVariationInfo().size(); i++)
{
std::cout << " " << std::dec << i << std::endl;
std::cout << " FirmwareVariationId: 0x" << std::hex << variation_info[i].variation_id << std::endl;
fmt::print(" {:d}\n", i);
fmt::print(" FirmwareVariationId: 0x{:x}\n", variation_info[i].variation_id);
if (mCnmt.getSystemUpdateMetaExtendedData().getFormatVersion() == 2)
{
std::cout << " ReferToBase: " << std::boolalpha << variation_info[i].meta.empty() << std::endl;
fmt::print(" ReferToBase: {}\n", variation_info[i].meta.empty());
if (variation_info[i].meta.empty() == false)
{
std::cout << " ContentMeta:" << std::endl;
fmt::print(" ContentMeta:\n");
displayContentMetaInfoList(variation_info[i].meta, " ");
}
}
}
}
}
std::cout << " Digest: " << fnd::SimpleTextOutput::arrayToString(mCnmt.getDigest().data(), mCnmt.getDigest().size(), false, "") << std::endl;
fmt::print(" Digest: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(mCnmt.getDigest().data(), mCnmt.getDigest().size(), false, ""));
}
void CnmtProcess::displayContentMetaInfo(const nn::hac::ContentMetaInfo& content_meta_info, const std::string& prefix)
void nstool::CnmtProcess::displayContentMetaInfo(const pie::hac::ContentMetaInfo& content_meta_info, const std::string& prefix)
{
std::cout << prefix << "Id: 0x" << std::hex << std::setw(16) << std::setfill('0') << content_meta_info.getTitleId() << std::endl;
std::cout << prefix << "Version: " << nn::hac::ContentMetaUtil::getVersionAsString(content_meta_info.getTitleVersion()) << " (v" << std::dec << content_meta_info.getTitleVersion() << ")"<< std::endl;
std::cout << prefix << "Type: " << nn::hac::ContentMetaUtil::getContentMetaTypeAsString(content_meta_info.getContentMetaType()) << " (" << std::dec << (uint32_t)content_meta_info.getContentMetaType() << ")" << std::endl;
std::cout << prefix << "Attributes: 0x" << std::hex << content_meta_info.getAttribute().to_ullong();
if (content_meta_info.getAttribute().any())
const pie::hac::sContentMetaInfo* content_meta_info_raw = (const pie::hac::sContentMetaInfo*)content_meta_info.getBytes().data();
fmt::print("{:s}Id: 0x{:016x}\n", prefix, content_meta_info.getTitleId());
fmt::print("{:s}Version: {:s} (v{:d})\n", prefix, pie::hac::ContentMetaUtil::getVersionAsString(content_meta_info.getTitleVersion()), content_meta_info.getTitleVersion());
fmt::print("{:s}Type: {:s} ({:d})\n", prefix, pie::hac::ContentMetaUtil::getContentMetaTypeAsString(content_meta_info.getContentMetaType()), (uint32_t)content_meta_info.getContentMetaType());
fmt::print("{:s}Attributes: 0x{:x}", prefix, *((byte_t*)&content_meta_info_raw->attributes) );
if (content_meta_info.getAttribute().size())
{
std::vector<std::string> attribute_list;
for (size_t flag = 0; flag < content_meta_info.getAttribute().size(); flag++)
for (auto itr = content_meta_info.getAttribute().begin(); itr != content_meta_info.getAttribute().end(); itr++)
{
if (content_meta_info.getAttribute().test(flag))
{
attribute_list.push_back(nn::hac::ContentMetaUtil::getContentMetaAttributeFlagAsString(nn::hac::cnmt::ContentMetaAttributeFlag(flag)));
}
attribute_list.push_back(pie::hac::ContentMetaUtil::getContentMetaAttributeFlagAsString(pie::hac::cnmt::ContentMetaAttributeFlag(*itr)));
}
std::cout << " [";
fmt::print(" [");
for (auto itr = attribute_list.begin(); itr != attribute_list.end(); itr++)
{
std::cout << *itr;
fmt::print("{:s}",*itr);
if ((itr + 1) != attribute_list.end())
{
std::cout << ", ";
fmt::print(", ");
}
}
std::cout << "]";
fmt::print("]");
}
fmt::print("\n");
}
void CnmtProcess::displayContentMetaInfoList(const std::vector<nn::hac::ContentMetaInfo>& content_meta_info_list, const std::string& prefix)
void nstool::CnmtProcess::displayContentMetaInfoList(const std::vector<pie::hac::ContentMetaInfo>& content_meta_info_list, const std::string& prefix)
{
for (size_t i = 0; i < content_meta_info_list.size(); i++)
{
const nn::hac::ContentMetaInfo& info = mCnmt.getContentMetaInfo()[i];
std::cout << prefix << std::dec << i << std::endl;
displayContentMetaInfo(info, prefix + " ");
std::cout << std::endl;
}
{
const pie::hac::ContentMetaInfo& info = mCnmt.getContentMetaInfo()[i];
fmt::print("{:s}{:d}\n", i);
displayContentMetaInfo(info, prefix + " ");
}
}

View file

@ -1,11 +1,9 @@
#pragma once
#include <string>
#include <fnd/types.h>
#include <fnd/IFile.h>
#include <fnd/SharedPtr.h>
#include <nn/hac/ContentMeta.h>
#include "types.h"
#include "common.h"
#include <pietendo/hac/ContentMeta.h>
namespace nstool {
class CnmtProcess
{
@ -14,24 +12,25 @@ public:
void process();
void setInputFile(const fnd::SharedPtr<fnd::IFile>& file);
void setInputFile(const std::shared_ptr<tc::io::IStream>& file);
void setCliOutputMode(CliOutputMode type);
void setVerifyMode(bool verify);
const nn::hac::ContentMeta& getContentMeta() const;
const pie::hac::ContentMeta& getContentMeta() const;
private:
const std::string kModuleName = "CnmtProcess";
std::string mModuleName;
fnd::SharedPtr<fnd::IFile> mFile;
std::shared_ptr<tc::io::IStream> mFile;
CliOutputMode mCliOutputMode;
bool mVerify;
nn::hac::ContentMeta mCnmt;
pie::hac::ContentMeta mCnmt;
void importCnmt();
void displayCnmt();
void displayContentMetaInfo(const nn::hac::ContentMetaInfo& content_meta_info, const std::string& prefix);
void displayContentMetaInfoList(const std::vector<nn::hac::ContentMetaInfo>& content_meta_info_list, const std::string& prefix);
};
void displayContentMetaInfo(const pie::hac::ContentMetaInfo& content_meta_info, const std::string& prefix);
void displayContentMetaInfoList(const std::vector<pie::hac::ContentMetaInfo>& content_meta_info_list, const std::string& prefix);
};
}

View file

@ -1,195 +0,0 @@
#include "CompressedArchiveIFile.h"
#include <fnd/lz4.h>
#include <iostream>
CompressedArchiveIFile::CompressedArchiveIFile(const fnd::SharedPtr<fnd::IFile>& base_file, size_t compression_meta_offset) :
mFile(base_file),
mCompEntries(),
mLogicalFileSize(0),
mCacheCapacity(nn::hac::compression::kRomfsBlockSize),
mCurrentCacheDataSize(0),
mCache(std::shared_ptr<byte_t>(new byte_t[mCacheCapacity])),
mScratch(std::shared_ptr<byte_t>(new byte_t[mCacheCapacity]))
{
// determine and check the compression metadata size
size_t compression_meta_size = (*mFile)->size() - compression_meta_offset;
if (compression_meta_size % sizeof(nn::hac::sCompressionEntry))
{
fnd::Exception(kModuleName, "Invalid compression meta size");
}
// import raw metadata
std::shared_ptr<byte_t> entries_raw = std::shared_ptr<byte_t>(new byte_t[compression_meta_size]);
(*mFile)->read(entries_raw.get(), compression_meta_offset, compression_meta_size);
// process metadata entries
nn::hac::sCompressionEntry* entries = (nn::hac::sCompressionEntry*)entries_raw.get();
for (size_t idx = 0, num = compression_meta_size / sizeof(nn::hac::sCompressionEntry); idx < num; idx++)
{
if (idx == 0)
{
if (entries[idx].physical_offset.get() != 0x0)
throw fnd::Exception(kModuleName, "Entry 0 had a non-zero physical offset");
if (entries[idx].virtual_offset.get() != 0x0)
throw fnd::Exception(kModuleName, "Entry 0 had a non-zero virtual offset");
}
else
{
if (entries[idx].physical_offset.get() != align(entries[idx - 1].physical_offset.get() + entries[idx - 1].physical_size.get(), nn::hac::compression::kRomfsBlockAlign))
throw fnd::Exception(kModuleName, "Entry was not physically aligned with previous entry");
if (entries[idx].virtual_offset.get() <= entries[idx - 1].virtual_offset.get())
throw fnd::Exception(kModuleName, "Entry was not virtually aligned with previous entry");
// set previous entry virtual_size = this->virtual_offset - prev->virtual_offset;
mCompEntries[mCompEntries.size() - 1].virtual_size = uint32_t(entries[idx].virtual_offset.get() - mCompEntries[mCompEntries.size() - 1].virtual_offset);
}
if (entries[idx].physical_size.get() > nn::hac::compression::kRomfsBlockSize)
throw fnd::Exception(kModuleName, "Entry physical size was too large");
switch ((nn::hac::compression::CompressionType)entries[idx].compression_type)
{
case (nn::hac::compression::CompressionType::None):
case (nn::hac::compression::CompressionType::Lz4):
break;
default:
throw fnd::Exception(kModuleName, "Unsupported CompressionType");
}
mCompEntries.push_back({(nn::hac::compression::CompressionType)entries[idx].compression_type, entries[idx].virtual_offset.get(), 0, entries[idx].physical_offset.get(), entries[idx].physical_size.get()});
}
// determine logical file size and final entry size
importEntryDataToCache(mCompEntries.size() - 1);
mCompEntries[mCurrentEntryIndex].virtual_size = mCurrentCacheDataSize;
mLogicalFileSize = mCompEntries[mCurrentEntryIndex].virtual_offset + mCompEntries[mCurrentEntryIndex].virtual_size;
/*
for (auto itr = mCompEntries.begin(); itr != mCompEntries.end(); itr++)
{
std::cout << "entry " << std::endl;
std::cout << " type: " << (uint32_t)itr->compression_type << std::endl;
std::cout << " phys_addr: 0x" << std::hex << itr->physical_offset << std::endl;
std::cout << " phys_size: 0x" << std::hex << itr->physical_size << std::endl;
std::cout << " virt_addr: 0x" << std::hex << itr->virtual_offset << std::endl;
std::cout << " virt_size: 0x" << std::hex << itr->virtual_size << std::endl;
}
std::cout << "logical size: 0x" << std::hex << mLogicalFileSize << std::endl;
*/
}
size_t CompressedArchiveIFile::size()
{
return mLogicalFileSize;
}
void CompressedArchiveIFile::seek(size_t offset)
{
mLogicalOffset = std::min<size_t>(offset, mLogicalFileSize);
}
void CompressedArchiveIFile::read(byte_t* out, size_t len)
{
// limit len to the end of the logical file
len = std::min<size_t>(len, mLogicalFileSize - mLogicalOffset);
for (size_t pos = 0, entry_index = getEntryIndexForLogicalOffset(mLogicalOffset); pos < len; entry_index++)
{
// importing entry into cache (this does nothing if the entry is already imported)
importEntryDataToCache(entry_index);
// write padding if required
if (mCompEntries[entry_index].virtual_size > mCurrentCacheDataSize)
{
memset(mCache.get() + mCurrentCacheDataSize, 0, mCompEntries[entry_index].virtual_size - mCurrentCacheDataSize);
}
// determine subset of cache to copy out
size_t read_offset = mLogicalOffset - (size_t)mCompEntries[entry_index].virtual_offset;
size_t read_size = std::min<size_t>(len, (size_t)mCompEntries[entry_index].virtual_size - read_offset);
memcpy(out + pos, mCache.get() + read_offset, read_size);
// update position/logical offset
pos += read_size;
mLogicalOffset += read_size;
}
}
void CompressedArchiveIFile::read(byte_t* out, size_t offset, size_t len)
{
seek(offset);
read(out, len);
}
void CompressedArchiveIFile::write(const byte_t* out, size_t len)
{
throw fnd::Exception(kModuleName, "write() not supported");
}
void CompressedArchiveIFile::write(const byte_t* out, size_t offset, size_t len)
{
throw fnd::Exception(kModuleName, "write() not supported");
}
void CompressedArchiveIFile::importEntryDataToCache(size_t entry_index)
{
// return if entry already imported
if (mCurrentEntryIndex == entry_index && mCurrentCacheDataSize != 0)
return;
// save index
mCurrentEntryIndex = entry_index;
// reference entry
CompressionEntry& entry = mCompEntries[mCurrentEntryIndex];
if (entry.compression_type == nn::hac::compression::CompressionType::None)
{
(*mFile)->read(mCache.get(), entry.physical_offset, entry.physical_size);
mCurrentCacheDataSize = entry.physical_size;
}
else if (entry.compression_type == nn::hac::compression::CompressionType::Lz4)
{
(*mFile)->read(mScratch.get(), entry.physical_offset, entry.physical_size);
mCurrentCacheDataSize = 0;
fnd::lz4::decompressData(mScratch.get(), entry.physical_size, mCache.get(), uint32_t(mCacheCapacity), mCurrentCacheDataSize);
if (mCurrentCacheDataSize == 0)
{
throw fnd::Exception(kModuleName, "Decompression of final block failed");
}
}
}
size_t CompressedArchiveIFile::getEntryIndexForLogicalOffset(size_t logical_offset)
{
// rule out bad offset
if (logical_offset > mLogicalFileSize)
throw fnd::Exception(kModuleName, "illegal logical offset");
size_t entry_index = 0;
// try the current comp entry
if (mCompEntries[mCurrentEntryIndex].virtual_offset <= logical_offset && \
mCompEntries[mCurrentEntryIndex].virtual_offset + mCompEntries[mCurrentEntryIndex].virtual_size >= logical_offset)
{
entry_index = mCurrentEntryIndex;
}
else
{
for (size_t index = 0; index < mCompEntries.size(); index++)
{
if (mCompEntries[index].virtual_offset <= logical_offset && \
mCompEntries[index].virtual_offset + mCompEntries[index].virtual_size >= logical_offset)
{
entry_index = index;
}
}
}
return entry_index;
}

View file

@ -1,51 +0,0 @@
#pragma once
#include <sstream>
#include <fnd/IFile.h>
#include <fnd/SharedPtr.h>
#include <memory>
#include <vector>
#include <nn/hac/define/compression.h>
class CompressedArchiveIFile : public fnd::IFile
{
public:
CompressedArchiveIFile(const fnd::SharedPtr<fnd::IFile>& file, size_t compression_meta_offset);
size_t size();
void seek(size_t offset);
void read(byte_t* out, size_t len);
void read(byte_t* out, size_t offset, size_t len);
void write(const byte_t* out, size_t len);
void write(const byte_t* out, size_t offset, size_t len);
private:
const std::string kModuleName = "CompressedArchiveIFile";
std::stringstream mErrorSs;
struct CompressionEntry
{
nn::hac::compression::CompressionType compression_type;
uint64_t virtual_offset;
uint32_t virtual_size;
uint64_t physical_offset;
uint32_t physical_size;
};
// raw data
fnd::SharedPtr<fnd::IFile> mFile;
// compression metadata
std::vector<CompressionEntry> mCompEntries;
size_t mLogicalFileSize;
size_t mLogicalOffset;
// cached decompressed entry
size_t mCacheCapacity; // capacity
size_t mCurrentEntryIndex; // index of entry currently associated with the cache
uint32_t mCurrentCacheDataSize; // size of data currently in cache
std::shared_ptr<byte_t> mCache; // where decompressed data resides
std::shared_ptr<byte_t> mScratch; // same size as cache, but is used for storing data pre-compression
// this will import entry to cache
void importEntryDataToCache(size_t entry_index);
size_t getEntryIndexForLogicalOffset(size_t logical_offset);
};

View file

@ -1,28 +1,29 @@
#include "ElfSymbolParser.h"
ElfSymbolParser::ElfSymbolParser()
nstool::ElfSymbolParser::ElfSymbolParser() :
mModuleName("nstool::ElfSymbolParser"),
mSymbolList()
{
mSymbolList.clear();
}
void ElfSymbolParser::operator=(const ElfSymbolParser& other)
void nstool::ElfSymbolParser::operator=(const ElfSymbolParser& other)
{
mSymbolList = other.mSymbolList;
}
bool ElfSymbolParser::operator==(const ElfSymbolParser& other) const
bool nstool::ElfSymbolParser::operator==(const ElfSymbolParser& other) const
{
return mSymbolList == other.mSymbolList;
}
bool ElfSymbolParser::operator!=(const ElfSymbolParser& other) const
bool nstool::ElfSymbolParser::operator!=(const ElfSymbolParser& other) const
{
return !(*this == other);
}
void ElfSymbolParser::parseData(const byte_t *dyn_sym, size_t dyn_sym_size, const byte_t *dyn_str, size_t dyn_str_size, bool is64Bit)
void nstool::ElfSymbolParser::parseData(const byte_t *dyn_sym, size_t dyn_sym_size, const byte_t *dyn_str, size_t dyn_str_size, bool is64Bit)
{
size_t dynSymSize = is64Bit ? sizeof(fnd::Elf64_Sym) : sizeof(fnd::Elf32_Sym);
size_t dynSymSize = is64Bit ? sizeof(Elf64_Sym) : sizeof(Elf32_Sym);
sElfSymbol symbol;
for (size_t i = 0; i < dyn_sym_size; i += dynSymSize)
@ -31,32 +32,32 @@ void ElfSymbolParser::parseData(const byte_t *dyn_sym, size_t dyn_sym_size, cons
if (is64Bit)
{
name_pos = le_word(((fnd::Elf64_Sym*)(dyn_sym + i))->st_name);
symbol.shn_index = le_hword(((fnd::Elf64_Sym*)(dyn_sym + i))->st_shndx);
symbol.symbol_type = fnd::elf::get_elf_st_type(((fnd::Elf64_Sym*)(dyn_sym + i))->st_info);
symbol.symbol_binding = fnd::elf::get_elf_st_bind(((fnd::Elf64_Sym*)(dyn_sym + i))->st_info);
name_pos = tc::bn::detail::__le_uint32(((Elf64_Sym*)(dyn_sym + i))->st_name);
symbol.shn_index = tc::bn::detail::__le_uint16(((Elf64_Sym*)(dyn_sym + i))->st_shndx);
symbol.symbol_type = elf::get_elf_st_type(((Elf64_Sym*)(dyn_sym + i))->st_info);
symbol.symbol_binding = elf::get_elf_st_bind(((Elf64_Sym*)(dyn_sym + i))->st_info);
}
else
{
name_pos = le_word(((fnd::Elf32_Sym*)(dyn_sym + i))->st_name);
symbol.shn_index = le_hword(((fnd::Elf32_Sym*)(dyn_sym + i))->st_shndx);
symbol.symbol_type = fnd::elf::get_elf_st_type(((fnd::Elf32_Sym*)(dyn_sym + i))->st_info);
symbol.symbol_binding = fnd::elf::get_elf_st_bind(((fnd::Elf32_Sym*)(dyn_sym + i))->st_info);
name_pos = tc::bn::detail::__le_uint32(((Elf32_Sym*)(dyn_sym + i))->st_name);
symbol.shn_index = tc::bn::detail::__le_uint16(((Elf32_Sym*)(dyn_sym + i))->st_shndx);
symbol.symbol_type = elf::get_elf_st_type(((Elf32_Sym*)(dyn_sym + i))->st_info);
symbol.symbol_binding = elf::get_elf_st_bind(((Elf32_Sym*)(dyn_sym + i))->st_info);
}
if (name_pos >= dyn_str_size)
{
throw fnd::Exception(kModuleName, "Out of bounds symbol name offset");
throw tc::Exception(mModuleName, "Out of bounds symbol name offset");
}
//for (; dyn_str[name_pos] == 0x00 && name_pos < dyn_str_size; name_pos++);
symbol.name = std::string((char*)&dyn_str[name_pos]);
mSymbolList.addElement(symbol);
mSymbolList.push_back(symbol);
}
}
const fnd::List<ElfSymbolParser::sElfSymbol>& ElfSymbolParser::getSymbolList() const
const std::vector<nstool::ElfSymbolParser::sElfSymbol>& nstool::ElfSymbolParser::getSymbolList() const
{
return mSymbolList;
}

View file

@ -1,7 +1,8 @@
#pragma once
#include <string>
#include <fnd/List.h>
#include <fnd/elf.h>
#include "types.h"
#include "elf.h"
namespace nstool {
class ElfSymbolParser
{
@ -40,10 +41,12 @@ public:
void parseData(const byte_t *dyn_sym, size_t dyn_sym_size, const byte_t *dyn_str, size_t dyn_str_size, bool is64Bit);
const fnd::List<sElfSymbol>& getSymbolList() const;
const std::vector<sElfSymbol>& getSymbolList() const;
private:
const std::string kModuleName = "ElfSymbolParser";
std::string mModuleName;
// data
fnd::List<sElfSymbol> mSymbolList;
};
std::vector<sElfSymbol> mSymbolList;
};
}

228
src/EsCertProcess.cpp Normal file
View file

@ -0,0 +1,228 @@
#include "EsCertProcess.h"
#include "PkiValidator.h"
#include "util.h"
#include <pietendo/hac/es/SignUtils.h>
nstool::EsCertProcess::EsCertProcess() :
mModuleName("nstool::EsCertProcess"),
mFile(),
mCliOutputMode(true, false, false, false),
mVerify(false)
{
}
void nstool::EsCertProcess::process()
{
importCerts();
if (mVerify)
validateCerts();
if (mCliOutputMode.show_basic_info)
displayCerts();
}
void nstool::EsCertProcess::setInputFile(const std::shared_ptr<tc::io::IStream>& file)
{
mFile = file;
}
void nstool::EsCertProcess::setKeyCfg(const KeyBag& keycfg)
{
mKeyCfg = keycfg;
}
void nstool::EsCertProcess::setCliOutputMode(CliOutputMode mode)
{
mCliOutputMode = mode;
}
void nstool::EsCertProcess::setVerifyMode(bool verify)
{
mVerify = verify;
}
void nstool::EsCertProcess::importCerts()
{
if (mFile == nullptr)
{
throw tc::Exception(mModuleName, "No file reader set.");
}
if (mFile->canRead() == false || mFile->canSeek() == false)
{
throw tc::NotSupportedException(mModuleName, "Input stream requires read/seek permissions.");
}
// check if file_size is greater than 20MB, don't import.
size_t file_size = tc::io::IOUtil::castInt64ToSize(mFile->length());
if (file_size > (0x100000 * 20))
{
throw tc::Exception(mModuleName, "File too large.");
}
// import certs
tc::ByteData scratch = tc::ByteData(file_size);
mFile->seek(0, tc::io::SeekOrigin::Begin);
mFile->read(scratch.data(), scratch.size());
pie::hac::es::SignedData<pie::hac::es::CertificateBody> cert;
for (size_t f_pos = 0; f_pos < scratch.size(); f_pos += cert.getBytes().size())
{
cert.fromBytes(scratch.data() + f_pos, scratch.size() - f_pos);
mCert.push_back(cert);
}
}
void nstool::EsCertProcess::validateCerts()
{
PkiValidator pki;
try
{
pki.setKeyCfg(mKeyCfg);
pki.addCertificates(mCert);
}
catch (const tc::Exception& e)
{
fmt::print("[WARNING] {}\n", e.error());
return;
}
}
void nstool::EsCertProcess::displayCerts()
{
for (size_t i = 0; i < mCert.size(); i++)
{
displayCert(mCert[i]);
}
}
void nstool::EsCertProcess::displayCert(const pie::hac::es::SignedData<pie::hac::es::CertificateBody>& cert)
{
fmt::print("[ES Certificate]\n");
fmt::print(" SignType {:s}", getSignTypeStr(cert.getSignature().getSignType()));
if (mCliOutputMode.show_extended_info)
fmt::print(" (0x{:x}) ({:s})", (uint32_t)cert.getSignature().getSignType(), getEndiannessStr(cert.getSignature().isLittleEndian()));
fmt::print("\n");
fmt::print(" Issuer: {:s}\n", cert.getBody().getIssuer());
fmt::print(" Subject: {:s}\n", cert.getBody().getSubject());
fmt::print(" PublicKeyType: {:s}", getPublicKeyTypeStr(cert.getBody().getPublicKeyType()));
if (mCliOutputMode.show_extended_info)
fmt::print(" ({:d})", (uint32_t)cert.getBody().getPublicKeyType());
fmt::print("\n");
fmt::print(" CertID: 0x{:x}\n", cert.getBody().getCertId());
if (cert.getBody().getPublicKeyType() == pie::hac::es::cert::RSA4096)
{
fmt::print(" PublicKey:\n");
if (mCliOutputMode.show_extended_info)
{
fmt::print(" Modulus:\n");
fmt::print(" {:s}", tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(cert.getBody().getRsa4096PublicKey().n.data(), cert.getBody().getRsa4096PublicKey().n.size(), true, "", 0x10, 6, false));
fmt::print(" Public Exponent:\n");
fmt::print(" {:s}", tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(cert.getBody().getRsa4096PublicKey().e.data(), cert.getBody().getRsa4096PublicKey().e.size(), true, "", 0x10, 6, false));
}
else
{
fmt::print(" Modulus:\n");
fmt::print(" {:s}\n", getTruncatedBytesString(cert.getBody().getRsa4096PublicKey().n.data(), cert.getBody().getRsa4096PublicKey().n.size()));
fmt::print(" Public Exponent:\n");
fmt::print(" {:s}\n", getTruncatedBytesString(cert.getBody().getRsa4096PublicKey().e.data(), cert.getBody().getRsa4096PublicKey().e.size()));
}
}
else if (cert.getBody().getPublicKeyType() == pie::hac::es::cert::RSA2048)
{
fmt::print(" PublicKey:\n");
if (mCliOutputMode.show_extended_info)
{
fmt::print(" Modulus:\n");
fmt::print(" {:s}", tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(cert.getBody().getRsa2048PublicKey().n.data(), cert.getBody().getRsa2048PublicKey().n.size(), true, "", 0x10, 6, false));
fmt::print(" Public Exponent:\n");
fmt::print(" {:s}", tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(cert.getBody().getRsa2048PublicKey().e.data(), cert.getBody().getRsa2048PublicKey().e.size(), true, "", 0x10, 6, false));
}
else
{
fmt::print(" Modulus:\n");
fmt::print(" {:s}\n", getTruncatedBytesString(cert.getBody().getRsa2048PublicKey().n.data(), cert.getBody().getRsa2048PublicKey().n.size()));
fmt::print(" Public Exponent:\n");
fmt::print(" {:s}\n", getTruncatedBytesString(cert.getBody().getRsa2048PublicKey().e.data(), cert.getBody().getRsa2048PublicKey().e.size()));
}
}
else if (cert.getBody().getPublicKeyType() == pie::hac::es::cert::ECDSA240)
{
fmt::print(" PublicKey:\n");
if (mCliOutputMode.show_extended_info)
{
fmt::print(" Modulus:\n");
fmt::print(" {:s}", tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(cert.getBody().getEcdsa240PublicKey().r.data(), cert.getBody().getEcdsa240PublicKey().r.size(), true, "", 0x10, 6, false));
fmt::print(" Public Exponent:\n");
fmt::print(" {:s}", tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(cert.getBody().getEcdsa240PublicKey().s.data(), cert.getBody().getEcdsa240PublicKey().s.size(), true, "", 0x10, 6, false));
}
else
{
fmt::print(" Modulus:\n");
fmt::print(" {:s}\n", getTruncatedBytesString(cert.getBody().getEcdsa240PublicKey().r.data(), cert.getBody().getEcdsa240PublicKey().r.size()));
fmt::print(" Public Exponent:\n");
fmt::print(" {:s}\n", getTruncatedBytesString(cert.getBody().getEcdsa240PublicKey().s.data(), cert.getBody().getEcdsa240PublicKey().s.size()));
}
}
}
std::string nstool::EsCertProcess::getSignTypeStr(pie::hac::es::sign::SignatureId type) const
{
std::string str;
switch (type)
{
case (pie::hac::es::sign::SIGN_ID_RSA4096_SHA1):
str = "RSA4096-SHA1";
break;
case (pie::hac::es::sign::SIGN_ID_RSA2048_SHA1):
str = "RSA2048-SHA1";
break;
case (pie::hac::es::sign::SIGN_ID_ECDSA240_SHA1):
str = "ECDSA240-SHA1";
break;
case (pie::hac::es::sign::SIGN_ID_RSA4096_SHA256):
str = "RSA4096-SHA256";
break;
case (pie::hac::es::sign::SIGN_ID_RSA2048_SHA256):
str = "RSA2048-SHA256";
break;
case (pie::hac::es::sign::SIGN_ID_ECDSA240_SHA256):
str = "ECDSA240-SHA256";
break;
default:
str = "Unknown";
break;
}
return str;
}
std::string nstool::EsCertProcess::getEndiannessStr(bool isLittleEndian) const
{
return isLittleEndian ? "LittleEndian" : "BigEndian";
}
std::string nstool::EsCertProcess::getPublicKeyTypeStr(pie::hac::es::cert::PublicKeyType type) const
{
std::string str;
switch (type)
{
case (pie::hac::es::cert::RSA4096):
str = "RSA4096";
break;
case (pie::hac::es::cert::RSA2048):
str = "RSA2048";
break;
case (pie::hac::es::cert::ECDSA240):
str = "ECDSA240";
break;
default:
str = "Unknown";
break;
}
return str;
}

42
src/EsCertProcess.h Normal file
View file

@ -0,0 +1,42 @@
#pragma once
#include "types.h"
#include "KeyBag.h"
#include <pietendo/hac/es/SignedData.h>
#include <pietendo/hac/es/CertificateBody.h>
namespace nstool {
class EsCertProcess
{
public:
EsCertProcess();
void process();
void setInputFile(const std::shared_ptr<tc::io::IStream>& file);
void setKeyCfg(const KeyBag& keycfg);
void setCliOutputMode(CliOutputMode type);
void setVerifyMode(bool verify);
private:
std::string mModuleName;
std::shared_ptr<tc::io::IStream> mFile;
KeyBag mKeyCfg;
CliOutputMode mCliOutputMode;
bool mVerify;
std::vector<pie::hac::es::SignedData<pie::hac::es::CertificateBody>> mCert;
void importCerts();
void validateCerts();
void displayCerts();
void displayCert(const pie::hac::es::SignedData<pie::hac::es::CertificateBody>& cert);
std::string getSignTypeStr(pie::hac::es::sign::SignatureId type) const;
std::string getEndiannessStr(bool isLittleEndian) const;
std::string getPublicKeyTypeStr(pie::hac::es::cert::PublicKeyType type) const;
};
}

View file

@ -1,85 +1,92 @@
#include <iostream>
#include <iomanip>
#include <fnd/SimpleTextOutput.h>
#include <fnd/OffsetAdjustedIFile.h>
#include <nn/pki/SignUtils.h>
#include "EsTikProcess.h"
#include "PkiValidator.h"
#include <pietendo/hac/es/SignUtils.h>
EsTikProcess::EsTikProcess() :
nstool::EsTikProcess::EsTikProcess() :
mModuleName("nstool::EsTikProcess"),
mFile(),
mCliOutputMode(_BIT(OUTPUT_BASIC)),
mCliOutputMode(true, false, false, false),
mVerify(false)
{
}
void EsTikProcess::process()
void nstool::EsTikProcess::process()
{
importTicket();
if (mVerify)
verifyTicket();
if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
if (mCliOutputMode.show_basic_info)
displayTicket();
}
void EsTikProcess::setInputFile(const fnd::SharedPtr<fnd::IFile>& file)
void nstool::EsTikProcess::setInputFile(const std::shared_ptr<tc::io::IStream>& file)
{
mFile = file;
}
void EsTikProcess::setKeyCfg(const KeyConfiguration& keycfg)
void nstool::EsTikProcess::setKeyCfg(const KeyBag& keycfg)
{
mKeyCfg = keycfg;
}
void EsTikProcess::setCertificateChain(const fnd::List<nn::pki::SignedData<nn::pki::CertificateBody>>& certs)
void nstool::EsTikProcess::setCertificateChain(const std::vector<pie::hac::es::SignedData<pie::hac::es::CertificateBody>>& certs)
{
mCerts = certs;
}
void EsTikProcess::setCliOutputMode(CliOutputMode mode)
void nstool::EsTikProcess::setCliOutputMode(CliOutputMode mode)
{
mCliOutputMode = mode;
}
void EsTikProcess::setVerifyMode(bool verify)
void nstool::EsTikProcess::setVerifyMode(bool verify)
{
mVerify = verify;
}
void EsTikProcess::importTicket()
void nstool::EsTikProcess::importTicket()
{
fnd::Vec<byte_t> scratch;
if (*mFile == nullptr)
if (mFile == nullptr)
{
throw fnd::Exception(kModuleName, "No file reader set.");
throw tc::Exception(mModuleName, "No file reader set.");
}
if (mFile->canRead() == false || mFile->canSeek() == false)
{
throw tc::NotSupportedException(mModuleName, "Input stream requires read/seek permissions.");
}
scratch.alloc((*mFile)->size());
(*mFile)->read(scratch.data(), 0, scratch.size());
// check if file_size is greater than 20MB, don't import.
size_t file_size = tc::io::IOUtil::castInt64ToSize(mFile->length());
if (file_size > (0x100000 * 20))
{
throw tc::Exception(mModuleName, "File too large.");
}
// read ticket
tc::ByteData scratch = tc::ByteData(file_size);
mFile->seek(0, tc::io::SeekOrigin::Begin);
mFile->read(scratch.data(), scratch.size());
mTik.fromBytes(scratch.data(), scratch.size());
}
void EsTikProcess::verifyTicket()
void nstool::EsTikProcess::verifyTicket()
{
PkiValidator pki_validator;
fnd::Vec<byte_t> tik_hash;
tc::ByteData tik_hash;
switch (nn::pki::sign::getHashAlgo(mTik.getSignature().getSignType()))
switch (pie::hac::es::sign::getHashAlgo(mTik.getSignature().getSignType()))
{
case (nn::pki::sign::HASH_ALGO_SHA1):
tik_hash.alloc(fnd::sha::kSha1HashLen);
fnd::sha::Sha1(mTik.getBody().getBytes().data(), mTik.getBody().getBytes().size(), tik_hash.data());
case (pie::hac::es::sign::HASH_ALGO_SHA1):
tik_hash = tc::ByteData(tc::crypto::Sha1Generator::kHashSize);
tc::crypto::GenerateSha1Hash(tik_hash.data(), mTik.getBody().getBytes().data(), mTik.getBody().getBytes().size());
break;
case (nn::pki::sign::HASH_ALGO_SHA256):
tik_hash.alloc(fnd::sha::kSha256HashLen);
fnd::sha::Sha256(mTik.getBody().getBytes().data(), mTik.getBody().getBytes().size(), tik_hash.data());
case (pie::hac::es::sign::HASH_ALGO_SHA256):
tik_hash = tc::ByteData(tc::crypto::Sha2256Generator::kHashSize);
tc::crypto::GenerateSha2256Hash(tik_hash.data(), mTik.getBody().getBytes().data(), mTik.getBody().getBytes().size());
break;
}
@ -89,105 +96,93 @@ void EsTikProcess::verifyTicket()
pki_validator.addCertificates(mCerts);
pki_validator.validateSignature(mTik.getBody().getIssuer(), mTik.getSignature().getSignType(), mTik.getSignature().getSignature(), tik_hash);
}
catch (const fnd::Exception& e)
catch (const tc::Exception& e)
{
std::cout << "[WARNING] Ticket signature could not be validated (" << e.error() << ")" << std::endl;
fmt::print("[WARNING] Ticket signature could not be validated ({:s})\n", e.error());
}
}
void EsTikProcess::displayTicket()
void nstool::EsTikProcess::displayTicket()
{
#define _SPLIT_VER(ver) (uint32_t)((ver>>10) & 0x3f) << "." << (uint32_t)((ver>>4) & 0x3f) << "." << (uint32_t)((ver>>0) & 0xf)
const pie::hac::es::TicketBody_V2& body = mTik.getBody();
const nn::es::TicketBody_V2& body = mTik.getBody();
fmt::print("[ES Ticket]\n");
fmt::print(" SignType: {:s}", getSignTypeStr(mTik.getSignature().getSignType()));
if (mCliOutputMode.show_extended_info)
fmt::print(" (0x{:x})", (uint32_t)mTik.getSignature().getSignType());
fmt::print("\n");
std::cout << "[ES Ticket]" << std::endl;
std::cout << " SignType: " << getSignTypeStr(mTik.getSignature().getSignType());
if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
std::cout << " (0x" << std::hex << mTik.getSignature().getSignType() << ")";
std::cout << std::endl;
std::cout << " Issuer: " << body.getIssuer() << std::endl;
std::cout << " Title Key:" << std::endl;
std::cout << " EncMode: " << getTitleKeyPersonalisationStr(body.getTitleKeyEncType()) << std::endl;
std::cout << " KeyGeneration: " << std::dec << (uint32_t)body.getCommonKeyId() << std::endl;
if (body.getTitleKeyEncType() == nn::es::ticket::RSA2048)
fmt::print(" Issuer: {:s}\n", body.getIssuer());
fmt::print(" Title Key:\n");
fmt::print(" EncMode: {:s}\n", getTitleKeyPersonalisationStr(body.getTitleKeyEncType()));
fmt::print(" KeyGeneration: {:d}\n", (uint32_t)body.getCommonKeyId());
if (body.getTitleKeyEncType() == pie::hac::es::ticket::RSA2048)
{
std::cout << " Data:" << std::endl;
for (size_t i = 0; i < 0x10; i++)
std::cout << " " << fnd::SimpleTextOutput::arrayToString(body.getEncTitleKey() + 0x10*i, 0x10, true, ":") << std::endl;
fmt::print(" Data:\n");
fmt::print(" {:s}", tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(body.getEncTitleKey(), 0x100, true, "", 0x10, 6, false));
}
else if (body.getTitleKeyEncType() == nn::es::ticket::AES128_CBC)
else if (body.getTitleKeyEncType() == pie::hac::es::ticket::AES128_CBC)
{
std::cout << " Data:" << std::endl;
std::cout << " " << fnd::SimpleTextOutput::arrayToString(body.getEncTitleKey(), 0x10, true, ":") << std::endl;
fmt::print(" Data:\n");
fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(body.getEncTitleKey(), 0x10, true, ""));
}
else
{
std::cout << " Data: <cannot display>" << std::endl;
fmt::print(" Data: <cannot display>\n");
}
std::cout << " Version: v" << _SPLIT_VER(body.getTicketVersion());
if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
std::cout << " (" << (uint32_t)body.getTicketVersion() << ")";
std::cout << std::endl;
std::cout << " License Type: " << getLicenseTypeStr(body.getLicenseType()) << std::endl;
if (body.getPropertyFlags().size() > 0)
fmt::print(" Version: {:s} (v{:d})\n", getTitleVersionStr(body.getTicketVersion()), body.getTicketVersion());
fmt::print(" License Type: {:s}\n", getLicenseTypeStr(body.getLicenseType()));
if (body.getPropertyFlags().size() > 0 || mCliOutputMode.show_extended_info)
{
std::cout << " Flags:" << std::endl;
pie::hac::es::sTicketBody_v2* raw_body = (pie::hac::es::sTicketBody_v2*)body.getBytes().data();
fmt::print(" PropertyMask: 0x{:04x}\n", ((tc::bn::le16<uint16_t>*)&raw_body->property_mask)->unwrap());
for (size_t i = 0; i < body.getPropertyFlags().size(); i++)
{
std::cout << " " << getPropertyFlagStr(body.getPropertyFlags()[i]) << std::endl;
fmt::print(" {:s}\n", getPropertyFlagStr(body.getPropertyFlags()[i]));
}
}
if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mCliOutputMode.show_extended_info)
{
std::cout << " Reserved Region:" << std::endl;
fnd::SimpleTextOutput::hexDump(body.getReservedRegion(), 8, 0x10, 4);
fmt::print(" Reserved Region:\n");
fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(body.getReservedRegion(), 8, true, ""));
}
if (body.getTicketId() != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
std::cout << " TicketId: 0x" << std::hex << std::setw(16) << std::setfill('0') << body.getTicketId() << std::endl;
if (body.getTicketId() != 0 || mCliOutputMode.show_extended_info)
fmt::print(" TicketId: 0x{:016x}\n", body.getTicketId());
if (body.getDeviceId() != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
std::cout << " DeviceId: 0x" << std::hex << std::setw(16) << std::setfill('0') << body.getDeviceId() << std::endl;
if (body.getDeviceId() != 0 || mCliOutputMode.show_extended_info)
fmt::print(" DeviceId: 0x{:016x}\n", body.getDeviceId());
std::cout << " RightsId: " << std::endl << " ";
fnd::SimpleTextOutput::hexDump(body.getRightsId(), 16);
fmt::print(" RightsId: \n");
fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(body.getRightsId(), 16, true, ""));
std::cout << " SectionTotalSize: 0x" << std::hex << body.getSectionTotalSize() << std::endl;
std::cout << " SectionHeaderOffset: 0x" << std::hex << body.getSectionHeaderOffset() << std::endl;
std::cout << " SectionNum: 0x" << std::hex << body.getSectionNum() << std::endl;
std::cout << " SectionEntrySize: 0x" << std::hex << body.getSectionEntrySize() << std::endl;
#undef _SPLIT_VER
fmt::print(" SectionTotalSize: 0x{:x}\n", body.getSectionTotalSize());
fmt::print(" SectionHeaderOffset: 0x{:x}\n", body.getSectionHeaderOffset());
fmt::print(" SectionNum: 0x{:x}\n", body.getSectionNum());
fmt::print(" SectionEntrySize: 0x{:x}\n", body.getSectionEntrySize());
}
const char* EsTikProcess::getSignTypeStr(uint32_t type) const
std::string nstool::EsTikProcess::getSignTypeStr(uint32_t type) const
{
const char* str = nullptr;
std::string str;
switch(type)
{
case (nn::pki::sign::SIGN_ID_RSA4096_SHA1):
case (pie::hac::es::sign::SIGN_ID_RSA4096_SHA1):
str = "RSA4096-SHA1";
break;
case (nn::pki::sign::SIGN_ID_RSA2048_SHA1):
case (pie::hac::es::sign::SIGN_ID_RSA2048_SHA1):
str = "RSA2048-SHA1";
break;
case (nn::pki::sign::SIGN_ID_ECDSA240_SHA1):
case (pie::hac::es::sign::SIGN_ID_ECDSA240_SHA1):
str = "ECDSA240-SHA1";
break;
case (nn::pki::sign::SIGN_ID_RSA4096_SHA256):
case (pie::hac::es::sign::SIGN_ID_RSA4096_SHA256):
str = "RSA4096-SHA256";
break;
case (nn::pki::sign::SIGN_ID_RSA2048_SHA256):
case (pie::hac::es::sign::SIGN_ID_RSA2048_SHA256):
str = "RSA2048-SHA256";
break;
case (nn::pki::sign::SIGN_ID_ECDSA240_SHA256):
case (pie::hac::es::sign::SIGN_ID_ECDSA240_SHA256):
str = "ECDSA240-SHA256";
break;
default:
@ -197,71 +192,85 @@ const char* EsTikProcess::getSignTypeStr(uint32_t type) const
return str;
}
const char* EsTikProcess::getTitleKeyPersonalisationStr(byte_t flag) const
std::string nstool::EsTikProcess::getTitleKeyPersonalisationStr(byte_t flag) const
{
const char* str = nullptr;
std::string str;
switch(flag)
{
case (nn::es::ticket::AES128_CBC):
case (pie::hac::es::ticket::AES128_CBC):
str = "Generic (AESCBC)";
break;
case (nn::es::ticket::RSA2048):
case (pie::hac::es::ticket::RSA2048):
str = "Personalised (RSA2048)";
break;
default:
str = "Unknown";
str = fmt::format("Unknown ({:d})", flag);
break;
}
return str;
}
const char* EsTikProcess::getLicenseTypeStr(byte_t flag) const
std::string nstool::EsTikProcess::getLicenseTypeStr(byte_t flag) const
{
const char* str = nullptr;
std::string str;
switch(flag)
{
case (nn::es::ticket::LICENSE_PERMANENT):
case (pie::hac::es::ticket::LICENSE_PERMANENT):
str = "Permanent";
break;
case (nn::es::ticket::LICENSE_DEMO):
case (pie::hac::es::ticket::LICENSE_DEMO):
str = "Demo";
break;
case (nn::es::ticket::LICENSE_TRIAL):
case (pie::hac::es::ticket::LICENSE_TRIAL):
str = "Trial";
break;
case (nn::es::ticket::LICENSE_RENTAL):
case (pie::hac::es::ticket::LICENSE_RENTAL):
str = "Rental";
break;
case (nn::es::ticket::LICENSE_SUBSCRIPTION):
case (pie::hac::es::ticket::LICENSE_SUBSCRIPTION):
str = "Subscription";
break;
case (nn::es::ticket::LICENSE_SERVICE):
case (pie::hac::es::ticket::LICENSE_SERVICE):
str = "Service";
break;
default:
str = "Unknown";
str = fmt::format("Unknown ({:d})", flag);
break;
}
return str;
}
const char* EsTikProcess::getPropertyFlagStr(byte_t flag) const
std::string nstool::EsTikProcess::getPropertyFlagStr(byte_t flag) const
{
const char* str = nullptr;
std::string str;
switch(flag)
{
case (nn::es::ticket::FLAG_PRE_INSTALL):
case (pie::hac::es::ticket::FLAG_PRE_INSTALL):
str = "PreInstall";
break;
case (nn::es::ticket::FLAG_SHARED_TITLE):
case (pie::hac::es::ticket::FLAG_SHARED_TITLE):
str = "SharedTitle";
break;
case (nn::es::ticket::FLAG_ALLOW_ALL_CONTENT):
case (pie::hac::es::ticket::FLAG_ALLOW_ALL_CONTENT):
str = "AllContent";
break;
case (pie::hac::es::ticket::FLAG_DEVICE_LINK_INDEPENDENT):
str = "DeviceLinkIndependent";
break;
case (pie::hac::es::ticket::FLAG_VOLATILE):
str = "Volatile";
break;
case (pie::hac::es::ticket::FLAG_ELICENSE_REQUIRED):
str = "ELicenseRequired";
break;
default:
str = "Unknown";
str = fmt::format("Unknown ({:d})", flag);
break;
}
return str;
}
std::string nstool::EsTikProcess::getTitleVersionStr(uint16_t version) const
{
return fmt::format("{:d}.{:d}.{:d}", ((version>>10) & 0x3f), ((version>>4) & 0x3f), ((version>>0) & 0xf));
}

View file

@ -1,14 +1,12 @@
#pragma once
#include <string>
#include <fnd/types.h>
#include <fnd/IFile.h>
#include <fnd/SharedPtr.h>
#include <fnd/Vec.h>
#include <nn/pki/SignedData.h>
#include <nn/pki/CertificateBody.h>
#include <nn/es/TicketBody_V2.h>
#include "KeyConfiguration.h"
#include "common.h"
#include "types.h"
#include "KeyBag.h"
#include <pietendo/hac/es/SignedData.h>
#include <pietendo/hac/es/CertificateBody.h>
#include <pietendo/hac/es/TicketBody_V2.h>
namespace nstool {
class EsTikProcess
{
@ -17,29 +15,31 @@ public:
void process();
void setInputFile(const fnd::SharedPtr<fnd::IFile>& file);
void setKeyCfg(const KeyConfiguration& keycfg);
void setCertificateChain(const fnd::List<nn::pki::SignedData<nn::pki::CertificateBody>>& certs);
void setInputFile(const std::shared_ptr<tc::io::IStream>& file);
void setKeyCfg(const KeyBag& keycfg);
void setCertificateChain(const std::vector<pie::hac::es::SignedData<pie::hac::es::CertificateBody>>& certs);
void setCliOutputMode(CliOutputMode mode);
void setVerifyMode(bool verify);
private:
const std::string kModuleName = "EsTikProcess";
std::string mModuleName;
fnd::SharedPtr<fnd::IFile> mFile;
KeyConfiguration mKeyCfg;
std::shared_ptr<tc::io::IStream> mFile;
KeyBag mKeyCfg;
CliOutputMode mCliOutputMode;
bool mVerify;
fnd::List<nn::pki::SignedData<nn::pki::CertificateBody>> mCerts;
std::vector<pie::hac::es::SignedData<pie::hac::es::CertificateBody>> mCerts;
nn::pki::SignedData<nn::es::TicketBody_V2> mTik;
pie::hac::es::SignedData<pie::hac::es::TicketBody_V2> mTik;
void importTicket();
void verifyTicket();
void displayTicket();
const char* getSignTypeStr(uint32_t type) const;
const char* getTitleKeyPersonalisationStr(byte_t flag) const;
const char* getLicenseTypeStr(byte_t flag) const;
const char* getPropertyFlagStr(byte_t flag) const;
};
std::string getSignTypeStr(uint32_t type) const;
std::string getTitleKeyPersonalisationStr(byte_t flag) const;
std::string getLicenseTypeStr(byte_t flag) const;
std::string getPropertyFlagStr(byte_t flag) const;
std::string getTitleVersionStr(uint16_t version) const;
};
}

258
src/FsProcess.cpp Normal file
View file

@ -0,0 +1,258 @@
#include "FsProcess.h"
#include "util.h"
#include <memory>
#include <tc/io/FileNotFoundException.h>
#include <tc/io/DirectoryNotFoundException.h>
nstool::FsProcess::FsProcess() :
mModuleLabel("nstool::FsProcess"),
mInputFs(),
mFsFormatName(),
mShowFsInfo(false),
mProperties(),
mShowFsTree(false),
mFsRootLabel(),
mExtractJobs(),
mDataCache(0x10000)
{
}
void nstool::FsProcess::process()
{
if (mInputFs == nullptr)
{
throw tc::InvalidOperationException(mModuleLabel, "No input filesystem");
}
if (mShowFsInfo)
{
fmt::print("[{:s}]\n", mFsFormatName.isSet() ? mFsFormatName.get() : "FileSystem/Info");
for (auto itr = mProperties.begin(); itr != mProperties.end(); itr++)
{
fmt::print(" {:s}\n", *itr);
}
}
if (mShowFsTree)
{
printFs();
}
if (mExtractJobs.empty() == false)
{
extractFs();
}
}
void nstool::FsProcess::setInputFileSystem(const std::shared_ptr<tc::io::IFileSystem>& input_fs)
{
mInputFs = input_fs;
}
void nstool::FsProcess::setFsFormatName(const std::string& fs_format_name)
{
mFsFormatName = fs_format_name;
}
void nstool::FsProcess::setShowFsInfo(bool show_fs_info)
{
mShowFsInfo = show_fs_info;
}
void nstool::FsProcess::setFsProperties(const std::vector<std::string>& properties)
{
mProperties = properties;
}
void nstool::FsProcess::setShowFsTree(bool show_fs_tree)
{
mShowFsTree = show_fs_tree;
}
void nstool::FsProcess::setFsRootLabel(const std::string& root_label)
{
mFsRootLabel = root_label;
}
void nstool::FsProcess::setExtractJobs(const std::vector<nstool::ExtractJob>& extract_jobs)
{
mExtractJobs = extract_jobs;
}
void nstool::FsProcess::printFs()
{
fmt::print("[{:s}/Tree]\n", (mFsFormatName.isSet() ? mFsFormatName.get() : "FileSystem"));
visitDir(tc::io::Path("/"), tc::io::Path("/"), false, true);
}
void nstool::FsProcess::extractFs()
{
fmt::print("[{:s}/Extract]\n", (mFsFormatName.isSet() ? mFsFormatName.get() : "FileSystem"));
for (auto itr = mExtractJobs.begin(); itr != mExtractJobs.end(); itr++)
{
// check if root path (legacy case)
if (itr->virtual_path == tc::io::Path("/"))
{
visitDir(tc::io::Path("/"), itr->extract_path, true, false);
//fmt::print("Root Dir Virtual Path: \"{:s}\"\n", itr->virtual_path.to_string());
// root directory extract successful, continue to next job
continue;
}
// otherwise determine if this is a file or subdirectory
try {
std::shared_ptr<tc::io::IStream> file_stream;
mInputFs->openFile(itr->virtual_path, tc::io::FileMode::Open, tc::io::FileAccess::Read, file_stream);
//fmt::print("Valid File Path: \"{:s}\"\n", itr->virtual_path.to_string());
// the output path for this file will depend on the user specified extract path
std::shared_ptr<tc::io::IFileSystem> local_fs = std::make_shared<tc::io::LocalFileSystem>(tc::io::LocalFileSystem());
// case: the extract_path is a valid path to an existing directory
// behaviour: extract the file, preserving the original filename, to the specified directory
// method: try getDirectoryListing(itr->extract_path), if this is does not throw, then we can be sure this is a valid path to a directory, file_extract_path = itr->extract_path + itr->virtual_path.back()
try {
tc::io::sDirectoryListing dir_listing;
local_fs->getDirectoryListing(itr->extract_path, dir_listing);
tc::io::Path file_extract_path = itr->extract_path + itr->virtual_path.back();
fmt::print("Saving {:s}...\n", file_extract_path.to_string());
writeStreamToFile(file_stream, itr->extract_path + itr->virtual_path.back(), mDataCache);
continue;
} catch (tc::io::DirectoryNotFoundException&) {
// acceptable exception, just means directory didn't exist
}
// case: the extract_path up until the last element is a valid path to an existing directory, but the full path specifies neither a directory or a file
// behaviour: treat extract_path as the intended location to write the extracted file (the original filename is not preserved, instead specified by the user in the final element of the extract path)
// method: since this checks n-1 elements, it implies a path with more than one element, so that must be accounted for, as relative paths are valid and single element paths aren't always root
try {
// get path to parent directory
tc::io::Path parent_dir_path = itr->extract_path;
// replace final path element with the current directory alias
parent_dir_path.pop_back(); // remove filename
parent_dir_path.push_back("."); // replace with the current dir name alias
// test parent directory exists
tc::io::sDirectoryListing dir_listing;
local_fs->getDirectoryListing(parent_dir_path, dir_listing);
fmt::print("Saving {:s} as {:s}...\n", itr->virtual_path.to_string(), itr->extract_path.to_string());
writeStreamToFile(file_stream, itr->extract_path, mDataCache);
continue;
} catch (tc::io::DirectoryNotFoundException&) {
// acceptable exception, just means the parent directory didn't exist
}
// extract path could not be determined, inform the user and skip this job
fmt::print("[WARNING] Extract path was invalid, and was skipped: {:s}\n", itr->extract_path.to_string());
continue;
} catch (tc::io::FileNotFoundException&) {
// acceptable exception, just means file didn't exist
}
// not a file, attempt to process this as a directory
try {
tc::io::sDirectoryListing dir_listing;
mInputFs->getDirectoryListing(itr->virtual_path, dir_listing);
visitDir(itr->virtual_path, itr->extract_path, true, false);
//fmt::print("Valid Directory Path: \"{:s}\"\n", itr->virtual_path.to_string());
// directory extract successful, continue to next job
continue;
} catch (tc::io::DirectoryNotFoundException&) {
// acceptable exception, just means directory didn't exist
}
fmt::print("[WARNING] Failed to extract virtual path: \"{:s}\"\n", itr->virtual_path.to_string());
}
}
void nstool::FsProcess::visitDir(const tc::io::Path& v_path, const tc::io::Path& l_path, bool extract_fs, bool print_fs)
{
tc::io::LocalFileSystem local_fs;
// get listing for directory
tc::io::sDirectoryListing info;
mInputFs->getDirectoryListing(v_path, info);
if (print_fs)
{
for (size_t i = 0; i < v_path.size(); i++)
fmt::print(" ");
fmt::print("{:s}/\n", ((v_path.size() == 1) ? (mFsRootLabel.isSet() ? (mFsRootLabel.get() + ":") : "Root:") : v_path.back()));
}
if (extract_fs)
{
// create local dir
local_fs.createDirectory(l_path);
}
// iterate thru child files
size_t cache_read_len;
tc::io::Path out_path;
std::shared_ptr<tc::io::IStream> in_stream;
std::shared_ptr<tc::io::IStream> out_stream;
for (auto itr = info.file_list.begin(); itr != info.file_list.end(); itr++)
{
if (print_fs)
{
for (size_t i = 0; i < v_path.size(); i++)
fmt::print(" ");
fmt::print(" {:s}\n", *itr);
}
if (extract_fs)
{
// build out path
out_path = l_path + *itr;
fmt::print("Saving {:s}...\n", out_path.to_string());
// begin export
mInputFs->openFile(v_path + *itr, tc::io::FileMode::Open, tc::io::FileAccess::Read, in_stream);
local_fs.openFile(out_path, tc::io::FileMode::OpenOrCreate, tc::io::FileAccess::Write, out_stream);
in_stream->seek(0, tc::io::SeekOrigin::Begin);
out_stream->seek(0, tc::io::SeekOrigin::Begin);
for (int64_t remaining_data = in_stream->length(); remaining_data > 0;)
{
cache_read_len = in_stream->read(mDataCache.data(), mDataCache.size());
if (cache_read_len == 0)
{
throw tc::io::IOException(mModuleLabel, fmt::format("Failed to read from {:s}file.", (mFsFormatName.isSet() ? (mFsFormatName.get() + " ") : "")));
}
out_stream->write(mDataCache.data(), cache_read_len);
remaining_data -= int64_t(cache_read_len);
}
}
}
// iterate thru child dirs
for (auto itr = info.dir_list.begin(); itr != info.dir_list.end(); itr++)
{
visitDir(v_path + *itr, l_path + *itr, extract_fs, print_fs);
}
}

50
src/FsProcess.h Normal file
View file

@ -0,0 +1,50 @@
#pragma once
#include <tc/Optional.h>
#include <tc/io.h>
#include "types.h"
namespace nstool
{
class FsProcess
{
public:
FsProcess();
void process();
void setInputFileSystem(const std::shared_ptr<tc::io::IFileSystem>& input_fs);
void setFsFormatName(const std::string& fs_format_name);
void setFsProperties(const std::vector<std::string>& properties);
void setShowFsInfo(bool show_fs_info);
void setShowFsTree(bool show_fs_tree);
void setFsRootLabel(const std::string& root_label);
void setExtractJobs(const std::vector<nstool::ExtractJob>& extract_jobs);
private:
std::string mModuleLabel;
std::shared_ptr<tc::io::IFileSystem> mInputFs;
// fs info
tc::Optional<std::string> mFsFormatName;
bool mShowFsInfo;
std::vector<std::string> mProperties;
// fs tree
bool mShowFsTree;
tc::Optional<std::string> mFsRootLabel;
// extract jobs
std::vector<nstool::ExtractJob> mExtractJobs;
// cache for file extract
tc::ByteData mDataCache;
void printFs();
void extractFs();
void visitDir(const tc::io::Path& v_path, const tc::io::Path& l_path, bool extract_fs, bool print_fs);
};
}

View file

@ -1,24 +1,31 @@
#include <iostream>
#include <iomanip>
#include <fnd/SimpleTextOutput.h>
#include <fnd/OffsetAdjustedIFile.h>
#include <nn/hac/GameCardUtil.h>
#include <nn/hac/ContentMetaUtil.h>
#include <nn/hac/ContentArchiveUtil.h>
#include "GameCardProcess.h"
GameCardProcess::GameCardProcess() :
#include <tc/crypto.h>
#include <tc/io/IOUtil.h>
#include <pietendo/hac/GameCardUtil.h>
#include <pietendo/hac/ContentMetaUtil.h>
#include <pietendo/hac/ContentArchiveUtil.h>
#include <pietendo/hac/GameCardFsSnapshotGenerator.h>
#include "FsProcess.h"
nstool::GameCardProcess::GameCardProcess() :
mModuleName("nstool::GameCardProcess"),
mFile(),
mCliOutputMode(_BIT(OUTPUT_BASIC)),
mCliOutputMode(true, false, false, false),
mVerify(false),
mListFs(false),
mIsTrueSdkXci(false),
mIsSdkXciEncrypted(false),
mGcHeaderOffset(0),
mProccessExtendedHeader(false),
mRootPfs(),
mExtractInfo()
mFileSystem(),
mFsProcess()
{
}
void GameCardProcess::process()
void nstool::GameCardProcess::process()
{
importHeader();
@ -27,257 +34,250 @@ void GameCardProcess::process()
validateXciSignature();
// display header
if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
if (mCliOutputMode.show_basic_info)
displayHeader();
// process root partition
// process nested HFS0
processRootPfs();
// process partitions
processPartitionPfs();
}
void GameCardProcess::setInputFile(const fnd::SharedPtr<fnd::IFile>& file)
void nstool::GameCardProcess::setInputFile(const std::shared_ptr<tc::io::IStream>& file)
{
mFile = file;
}
void GameCardProcess::setKeyCfg(const KeyConfiguration& keycfg)
void nstool::GameCardProcess::setKeyCfg(const KeyBag& keycfg)
{
mKeyCfg = keycfg;
}
void GameCardProcess::setCliOutputMode(CliOutputMode type)
void nstool::GameCardProcess::setCliOutputMode(CliOutputMode type)
{
mCliOutputMode = type;
}
void GameCardProcess::setVerifyMode(bool verify)
void nstool::GameCardProcess::setVerifyMode(bool verify)
{
mVerify = verify;
}
void GameCardProcess::setPartitionForExtract(const std::string& partition_name, const std::string& extract_path)
void nstool::GameCardProcess::setShowFsTree(bool show_fs_tree)
{
mExtractInfo.addElement({partition_name, extract_path});
mFsProcess.setShowFsTree(show_fs_tree);
}
void GameCardProcess::setListFs(bool list_fs)
void nstool::GameCardProcess::setExtractJobs(const std::vector<nstool::ExtractJob> extract_jobs)
{
mListFs = list_fs;
mFsProcess.setExtractJobs(extract_jobs);
}
void GameCardProcess::importHeader()
void nstool::GameCardProcess::importHeader()
{
fnd::Vec<byte_t> scratch;
if (*mFile == nullptr)
if (mFile == nullptr)
{
throw fnd::Exception(kModuleName, "No file reader set.");
throw tc::Exception(mModuleName, "No file reader set.");
}
if (mFile->canRead() == false || mFile->canSeek() == false)
{
throw tc::NotSupportedException(mModuleName, "Input stream requires read/seek permissions.");
}
// check stream is large enough for header
if (mFile->length() < tc::io::IOUtil::castSizeToInt64(sizeof(pie::hac::sSdkGcHeader)))
{
throw tc::Exception(mModuleName, "Corrupt GameCard Image: File too small.");
}
// allocate memory for header
scratch.alloc(sizeof(nn::hac::sSdkGcHeader));
tc::ByteData scratch = tc::ByteData(sizeof(pie::hac::sSdkGcHeader));
// read header region
(*mFile)->read((byte_t*)scratch.data(), 0, sizeof(nn::hac::sSdkGcHeader));
mFile->seek(0, tc::io::SeekOrigin::Begin);
mFile->read(scratch.data(), scratch.size());
// determine if this is a SDK XCI or a "Community" XCI
if (((nn::hac::sSdkGcHeader*)scratch.data())->signed_header.header.st_magic.get() == nn::hac::gc::kGcHeaderStructMagic)
if (((pie::hac::sSdkGcHeader*)scratch.data())->signed_header.header.st_magic.unwrap() == pie::hac::gc::kGcHeaderStructMagic)
{
mIsTrueSdkXci = true;
mGcHeaderOffset = sizeof(nn::hac::sGcKeyDataRegion);
mGcHeaderOffset = sizeof(pie::hac::sGcKeyDataRegion);
}
else if (((nn::hac::sGcHeader_Rsa2048Signed*)scratch.data())->header.st_magic.get() == nn::hac::gc::kGcHeaderStructMagic)
else if (((pie::hac::sGcHeader_Rsa2048Signed*)scratch.data())->header.st_magic.unwrap() == pie::hac::gc::kGcHeaderStructMagic)
{
mIsTrueSdkXci = false;
mGcHeaderOffset = 0;
}
else
{
throw fnd::Exception(kModuleName, "GameCard image did not have expected magic bytes");
throw tc::Exception(mModuleName, "Corrupt GameCard Image: Unexpected magic bytes.");
}
nn::hac::sGcHeader_Rsa2048Signed* hdr_ptr = (nn::hac::sGcHeader_Rsa2048Signed*)(scratch.data() + mGcHeaderOffset);
pie::hac::sGcHeader_Rsa2048Signed* hdr_ptr = (pie::hac::sGcHeader_Rsa2048Signed*)(scratch.data() + mGcHeaderOffset);
// generate hash of raw header
fnd::sha::Sha256((byte_t*)&hdr_ptr->header, sizeof(nn::hac::sGcHeader), mHdrHash.bytes);
tc::crypto::GenerateSha2256Hash(mHdrHash.data(), (byte_t*)&hdr_ptr->header, sizeof(pie::hac::sGcHeader));
// save the signature
memcpy(mHdrSignature, hdr_ptr->signature, fnd::rsa::kRsa2048Size);
memcpy(mHdrSignature.data(), hdr_ptr->signature.data(), mHdrSignature.size());
// decrypt extended header
fnd::aes::sAes128Key header_key;
if (mKeyCfg.getXciHeaderKey(header_key))
byte_t xci_header_key_index = hdr_ptr->header.key_flag & 0xf;
if (mKeyCfg.xci_header_key.find(xci_header_key_index) != mKeyCfg.xci_header_key.end())
{
nn::hac::GameCardUtil::decryptXciHeader(&hdr_ptr->header, header_key.key);
pie::hac::GameCardUtil::decryptXciHeader(&hdr_ptr->header, mKeyCfg.xci_header_key[xci_header_key_index].data());
mProccessExtendedHeader = true;
}
// deserialise header
mHdr.fromBytes((byte_t*)&hdr_ptr->header, sizeof(nn::hac::sGcHeader));
mHdr.fromBytes((byte_t*)&hdr_ptr->header, sizeof(pie::hac::sGcHeader));
}
void GameCardProcess::displayHeader()
void nstool::GameCardProcess::displayHeader()
{
std::cout << "[GameCard Header]" << std::endl;
std::cout << " CardHeaderVersion: " << std::dec << (uint32_t)mHdr.getCardHeaderVersion() << std::endl;
std::cout << " RomSize: " << nn::hac::GameCardUtil::getRomSizeAsString((nn::hac::gc::RomSize)mHdr.getRomSizeType());
if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
std::cout << " (0x" << std::hex << (uint32_t)mHdr.getRomSizeType() << ")";
std::cout << std::endl;
std::cout << " PackageId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mHdr.getPackageId() << std::endl;
std::cout << " Flags: 0x" << std::dec << (uint32_t)mHdr.getFlags() << std::endl;
if (mHdr.getFlags() != 0)
const pie::hac::sGcHeader* raw_hdr = (const pie::hac::sGcHeader*)mHdr.getBytes().data();
fmt::print("[GameCard/Header]\n");
fmt::print(" CardHeaderVersion: {:d}\n", mHdr.getCardHeaderVersion());
fmt::print(" RomSize: {:s}", pie::hac::GameCardUtil::getRomSizeAsString((pie::hac::gc::RomSize)mHdr.getRomSizeType()));
if (mCliOutputMode.show_extended_info)
fmt::print(" (0x{:x})", mHdr.getRomSizeType());
fmt::print("\n");
fmt::print(" PackageId: 0x{:016x}\n", mHdr.getPackageId());
fmt::print(" Flags: 0x{:02x}\n", *((byte_t*)&raw_hdr->flags));
for (auto itr = mHdr.getFlags().begin(); itr != mHdr.getFlags().end(); itr++)
{
for (uint32_t i = 0; i < 8; i++)
{
if (_HAS_BIT(mHdr.getFlags(), i))
{
std::cout << " " << nn::hac::GameCardUtil::getHeaderFlagsAsString((nn::hac::gc::HeaderFlags)i) << std::endl;
}
}
fmt::print(" {:s}\n", pie::hac::GameCardUtil::getHeaderFlagsAsString((pie::hac::gc::HeaderFlags)*itr));
}
if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mCliOutputMode.show_extended_info)
{
std::cout << " KekIndex: " << nn::hac::GameCardUtil::getKekIndexAsString((nn::hac::gc::KekIndex)mHdr.getKekIndex()) << " (" << std::dec << (uint32_t)mHdr.getKekIndex() << ")" << std::endl;
std::cout << " TitleKeyDecIndex: " << std::dec << (uint32_t)mHdr.getTitleKeyDecIndex() << std::endl;
std::cout << " InitialData:" << std::endl;
std::cout << " Hash:" << std::endl;
std::cout << " " << fnd::SimpleTextOutput::arrayToString(mHdr.getInitialDataHash().bytes, 0x10, true, ":") << std::endl;
std::cout << " " << fnd::SimpleTextOutput::arrayToString(mHdr.getInitialDataHash().bytes+0x10, 0x10, true, ":") << std::endl;
fmt::print(" KekIndex: {:s} ({:d})\n", pie::hac::GameCardUtil::getKekIndexAsString((pie::hac::gc::KekIndex)mHdr.getKekIndex()), mHdr.getKekIndex());
fmt::print(" TitleKeyDecIndex: {:d}\n", mHdr.getTitleKeyDecIndex());
fmt::print(" InitialData:\n");
fmt::print(" Hash:\n");
fmt::print(" {:s}", tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(mHdr.getInitialDataHash().data(), mHdr.getInitialDataHash().size(), true, "", 0x10, 6, false));
}
if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mCliOutputMode.show_extended_info)
{
std::cout << " Extended Header AesCbc IV:" << std::endl;
std::cout << " " << fnd::SimpleTextOutput::arrayToString(mHdr.getAesCbcIv().iv, sizeof(mHdr.getAesCbcIv().iv), true, ":") << std::endl;
fmt::print(" Extended Header AesCbc IV:\n");
fmt::print(" {:s}\n", tc::cli::FormatUtil::formatBytesAsString(mHdr.getAesCbcIv().data(), mHdr.getAesCbcIv().size(), true, ""));
}
std::cout << " SelSec: 0x" << std::hex << mHdr.getSelSec() << std::endl;
std::cout << " SelT1Key: 0x" << std::hex << mHdr.getSelT1Key() << std::endl;
std::cout << " SelKey: 0x" << std::hex << mHdr.getSelKey() << std::endl;
if (_HAS_BIT(mCliOutputMode, OUTPUT_LAYOUT))
fmt::print(" SelSec: 0x{:x}\n", mHdr.getSelSec());
fmt::print(" SelT1Key: 0x{:x}\n", mHdr.getSelT1Key());
fmt::print(" SelKey: 0x{:x}\n", mHdr.getSelKey());
if (mCliOutputMode.show_layout)
{
std::cout << " RomAreaStartPage: 0x" << std::hex << mHdr.getRomAreaStartPage();
fmt::print(" RomAreaStartPage: 0x{:x}", mHdr.getRomAreaStartPage());
if (mHdr.getRomAreaStartPage() != (uint32_t)(-1))
std::cout << " (0x" << std::hex << nn::hac::GameCardUtil::blockToAddr(mHdr.getRomAreaStartPage()) << ")";
std::cout << std::endl;
fmt::print(" (0x{:x})", pie::hac::GameCardUtil::blockToAddr(mHdr.getRomAreaStartPage()));
fmt::print("\n");
std::cout << " BackupAreaStartPage: 0x" << std::hex << mHdr.getBackupAreaStartPage();
fmt::print(" BackupAreaStartPage: 0x{:x}", mHdr.getBackupAreaStartPage());
if (mHdr.getBackupAreaStartPage() != (uint32_t)(-1))
std::cout << " (0x" << std::hex << nn::hac::GameCardUtil::blockToAddr(mHdr.getBackupAreaStartPage()) << ")";
std::cout << std::endl;
fmt::print(" (0x{:x})", pie::hac::GameCardUtil::blockToAddr(mHdr.getBackupAreaStartPage()));
fmt::print("\n");
std::cout << " ValidDataEndPage: 0x" << std::hex << mHdr.getValidDataEndPage();
fmt::print(" ValidDataEndPage: 0x{:x}", mHdr.getValidDataEndPage());
if (mHdr.getValidDataEndPage() != (uint32_t)(-1))
std::cout << " (0x" << std::hex << nn::hac::GameCardUtil::blockToAddr(mHdr.getValidDataEndPage()) << ")";
std::cout << std::endl;
fmt::print(" (0x{:x})", pie::hac::GameCardUtil::blockToAddr(mHdr.getValidDataEndPage()));
fmt::print("\n");
std::cout << " LimArea: 0x" << std::hex << mHdr.getLimAreaPage();
fmt::print(" LimArea: 0x{:x}", mHdr.getLimAreaPage());
if (mHdr.getLimAreaPage() != (uint32_t)(-1))
std::cout << " (0x" << std::hex << nn::hac::GameCardUtil::blockToAddr(mHdr.getLimAreaPage()) << ")";
std::cout << std::endl;
fmt::print(" (0x{:x})", pie::hac::GameCardUtil::blockToAddr(mHdr.getLimAreaPage()));
fmt::print("\n");
std::cout << " PartitionFs Header:" << std::endl;
std::cout << " Offset: 0x" << std::hex << mHdr.getPartitionFsAddress() << std::endl;
std::cout << " Size: 0x" << std::hex << mHdr.getPartitionFsSize() << std::endl;
if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
fmt::print(" PartitionFs Header:\n");
fmt::print(" Offset: 0x{:x}\n", mHdr.getPartitionFsAddress());
fmt::print(" Size: 0x{:x}\n", mHdr.getPartitionFsSize());
if (mCliOutputMode.show_extended_info)
{
std::cout << " Hash:" << std::endl;
std::cout << " " << fnd::SimpleTextOutput::arrayToString(mHdr.getPartitionFsHash().bytes, 0x10, true, ":") << std::endl;
std::cout << " " << fnd::SimpleTextOutput::arrayToString(mHdr.getPartitionFsHash().bytes+0x10, 0x10, true, ":") << std::endl;
fmt::print(" Hash:\n");
fmt::print(" {:s}", tc::cli::FormatUtil::formatBytesAsStringWithLineLimit(mHdr.getPartitionFsHash().data(), mHdr.getPartitionFsHash().size(), true, "", 0x10, 6, false));
}
}
if (mProccessExtendedHeader)
{
std::cout << "[GameCard Extended Header]" << std::endl;
std::cout << " FwVersion: v" << std::dec << mHdr.getFwVersion() << " (" << nn::hac::GameCardUtil::getCardFwVersionDescriptionAsString((nn::hac::gc::FwVersion)mHdr.getFwVersion()) << ")" << std::endl;
std::cout << " AccCtrl1: 0x" << std::hex << mHdr.getAccCtrl1() << std::endl;
std::cout << " CardClockRate: " << nn::hac::GameCardUtil::getCardClockRateAsString((nn::hac::gc::CardClockRate)mHdr.getAccCtrl1()) << std::endl;
std::cout << " Wait1TimeRead: 0x" << std::hex << mHdr.getWait1TimeRead() << std::endl;
std::cout << " Wait2TimeRead: 0x" << std::hex << mHdr.getWait2TimeRead() << std::endl;
std::cout << " Wait1TimeWrite: 0x" << std::hex << mHdr.getWait1TimeWrite() << std::endl;
std::cout << " Wait2TimeWrite: 0x" << std::hex << mHdr.getWait2TimeWrite() << std::endl;
std::cout << " SdkAddon Version: " << nn::hac::ContentArchiveUtil::getSdkAddonVersionAsString(mHdr.getFwMode()) << " (v" << std::dec << mHdr.getFwMode() << ")" << std::endl;
std::cout << " CompatibilityType: " << nn::hac::GameCardUtil::getCompatibilityTypeAsString((nn::hac::gc::CompatibilityType)mHdr.getCompatibilityType()) << " (" << std::dec << (uint32_t) mHdr.getCompatibilityType() << ")" << std::endl;
std::cout << " Update Partition Info:" << std::endl;
std::cout << " CUP Version: " << nn::hac::ContentMetaUtil::getVersionAsString(mHdr.getUppVersion()) << " (v" << std::dec << mHdr.getUppVersion() << ")" << std::endl;
std::cout << " CUP TitleId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mHdr.getUppId() << std::endl;
std::cout << " CUP Digest: " << fnd::SimpleTextOutput::arrayToString(mHdr.getUppHash(), 8, true, ":") << std::endl;
fmt::print("[GameCard/ExtendedHeader]\n");
fmt::print(" FwVersion: v{:d} ({:s})\n", mHdr.getFwVersion(), pie::hac::GameCardUtil::getCardFwVersionDescriptionAsString((pie::hac::gc::FwVersion)mHdr.getFwVersion()));
fmt::print(" AccCtrl1: 0x{:x}\n", mHdr.getAccCtrl1());
fmt::print(" CardClockRate: {:s}\n", pie::hac::GameCardUtil::getCardClockRateAsString((pie::hac::gc::CardClockRate)mHdr.getAccCtrl1()));
fmt::print(" Wait1TimeRead: 0x{:x}\n", mHdr.getWait1TimeRead());
fmt::print(" Wait2TimeRead: 0x{:x}\n", mHdr.getWait2TimeRead());
fmt::print(" Wait1TimeWrite: 0x{:x}\n", mHdr.getWait1TimeWrite());
fmt::print(" Wait2TimeWrite: 0x{:x}\n", mHdr.getWait2TimeWrite());
fmt::print(" SdkAddon Version: {:s} (v{:d})\n", pie::hac::ContentArchiveUtil::getSdkAddonVersionAsString(mHdr.getFwMode()), mHdr.getFwMode());
fmt::print(" CompatibilityType: {:s} ({:d})\n", pie::hac::GameCardUtil::getCompatibilityTypeAsString((pie::hac::gc::CompatibilityType)mHdr.getCompatibilityType()), mHdr.getCompatibilityType());
fmt::print(" Update Partition Info:\n");
fmt::print(" CUP Version: {:s} (v{:d})\n", pie::hac::ContentMetaUtil::getVersionAsString(mHdr.getUppVersion()), mHdr.getUppVersion());
fmt::print(" CUP TitleId: 0x{:016x}\n", mHdr.getUppId());
fmt::print(" CUP Digest: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(mHdr.getUppHash().data(), mHdr.getUppHash().size(), true, ""));
}
}
bool GameCardProcess::validateRegionOfFile(size_t offset, size_t len, const byte_t* test_hash, bool use_salt, byte_t salt)
bool nstool::GameCardProcess::validateRegionOfFile(int64_t offset, int64_t len, const byte_t* test_hash, bool use_salt, byte_t salt)
{
fnd::Vec<byte_t> scratch;
fnd::sha::sSha256Hash calc_hash;
// read region into memory
tc::ByteData scratch = tc::ByteData(tc::io::IOUtil::castInt64ToSize(len));
mFile->seek(offset, tc::io::SeekOrigin::Begin);
mFile->read(scratch.data(), scratch.size());
// update hash
tc::crypto::Sha2256Generator sha256_gen;
sha256_gen.initialize();
sha256_gen.update(scratch.data(), scratch.size());
if (use_salt)
{
scratch.alloc(len + 1);
scratch.data()[len] = salt;
}
else
{
scratch.alloc(len);
}
sha256_gen.update(&salt, sizeof(salt));
(*mFile)->read(scratch.data(), offset, len);
fnd::sha::Sha256(scratch.data(), scratch.size(), calc_hash.bytes);
// calculate hash
pie::hac::detail::sha256_hash_t calc_hash;
sha256_gen.getHash(calc_hash.data());
return calc_hash.compare(test_hash);
return memcmp(calc_hash.data(), test_hash, calc_hash.size()) == 0;
}
bool GameCardProcess::validateRegionOfFile(size_t offset, size_t len, const byte_t* test_hash)
bool nstool::GameCardProcess::validateRegionOfFile(int64_t offset, int64_t len, const byte_t* test_hash)
{
return validateRegionOfFile(offset, len, test_hash, false, 0);
}
void GameCardProcess::validateXciSignature()
void nstool::GameCardProcess::validateXciSignature()
{
fnd::rsa::sRsa2048Key header_sign_key;
mKeyCfg.getXciHeaderSignKey(header_sign_key);
if (fnd::rsa::pkcs::rsaVerify(header_sign_key, fnd::sha::HASH_SHA256, mHdrHash.bytes, mHdrSignature) != 0)
if (mKeyCfg.xci_header_sign_key.isSet())
{
std::cout << "[WARNING] GameCard Header Signature: FAIL" << std::endl;
}
}
void GameCardProcess::processRootPfs()
{
if (mVerify && validateRegionOfFile(mHdr.getPartitionFsAddress(), mHdr.getPartitionFsSize(), mHdr.getPartitionFsHash().bytes, mHdr.getCompatibilityType() != nn::hac::gc::COMPAT_GLOBAL, mHdr.getCompatibilityType()) == false)
{
std::cout << "[WARNING] GameCard Root HFS0: FAIL (bad hash)" << std::endl;
}
mRootPfs.setInputFile(new fnd::OffsetAdjustedIFile(mFile, mHdr.getPartitionFsAddress(), mHdr.getPartitionFsSize()));
mRootPfs.setListFs(mListFs);
mRootPfs.setVerifyMode(false);
mRootPfs.setCliOutputMode(mCliOutputMode);
mRootPfs.setMountPointName(kXciMountPointName);
mRootPfs.process();
}
void GameCardProcess::processPartitionPfs()
{
const fnd::List<nn::hac::PartitionFsHeader::sFile>& rootPartitions = mRootPfs.getPfsHeader().getFileList();
for (size_t i = 0; i < rootPartitions.size(); i++)
{
// this must be validated here because only the size of the root partiton header is known at verification time
if (mVerify && validateRegionOfFile(mHdr.getPartitionFsAddress() + rootPartitions[i].offset, rootPartitions[i].hash_protected_size, rootPartitions[i].hash.bytes) == false)
if (tc::crypto::VerifyRsa2048Pkcs1Sha2256(mHdrSignature.data(), mHdrHash.data(), mKeyCfg.xci_header_sign_key.get()) == false)
{
std::cout << "[WARNING] GameCard " << rootPartitions[i].name << " Partition HFS0: FAIL (bad hash)" << std::endl;
fmt::print("[WARNING] GameCard Header Signature: FAIL\n");
}
PfsProcess tmp;
tmp.setInputFile(new fnd::OffsetAdjustedIFile(mFile, mHdr.getPartitionFsAddress() + rootPartitions[i].offset, rootPartitions[i].size));
tmp.setListFs(mListFs);
tmp.setVerifyMode(mVerify);
tmp.setCliOutputMode(mCliOutputMode);
tmp.setMountPointName(kXciMountPointName + rootPartitions[i].name);
if (mExtractInfo.hasElement<std::string>(rootPartitions[i].name))
tmp.setExtractPath(mExtractInfo.getElement<std::string>(rootPartitions[i].name).extract_path);
tmp.process();
}
else
{
fmt::print("[WARNING] GameCard Header Signature: FAIL (Failed to load rsa public key.)\n");
}
}
void nstool::GameCardProcess::processRootPfs()
{
if (mVerify && validateRegionOfFile(mHdr.getPartitionFsAddress(), mHdr.getPartitionFsSize(), mHdr.getPartitionFsHash().data(), mHdr.getCompatibilityType() != pie::hac::gc::CompatibilityType_Global, mHdr.getCompatibilityType()) == false)
{
fmt::print("[WARNING] GameCard Root HFS0: FAIL (bad hash)\n");
}
std::shared_ptr<tc::io::IStream> gc_fs_raw = std::make_shared<tc::io::SubStream>(tc::io::SubStream(mFile, mHdr.getPartitionFsAddress(), pie::hac::GameCardUtil::blockToAddr(mHdr.getValidDataEndPage()+1) - mHdr.getPartitionFsAddress()));
auto gc_vfs_snapshot = pie::hac::GameCardFsSnapshotGenerator(gc_fs_raw, mHdr.getPartitionFsSize(), mVerify ? pie::hac::GameCardFsSnapshotGenerator::ValidationMode_Warn : pie::hac::GameCardFsSnapshotGenerator::ValidationMode_None);
mFileSystem = std::make_shared<tc::io::VirtualFileSystem>(tc::io::VirtualFileSystem(gc_vfs_snapshot) );
mFsProcess.setInputFileSystem(mFileSystem);
mFsProcess.setFsFormatName("PartitionFs");
mFsProcess.setFsProperties({
fmt::format("Type: Nested HFS0"),
fmt::format("DirNum: {:d}", gc_vfs_snapshot.dir_entries.empty() ? 0 : gc_vfs_snapshot.dir_entries.size() - 1), // -1 to not include root directory
fmt::format("FileNum: {:d}", gc_vfs_snapshot.file_entries.size())
});
mFsProcess.setShowFsInfo(mCliOutputMode.show_basic_info);
mFsProcess.setFsRootLabel(kXciMountPointName);
mFsProcess.process();
}

View file

@ -1,14 +1,11 @@
#pragma once
#include <string>
#include <fnd/types.h>
#include <fnd/IFile.h>
#include <fnd/SharedPtr.h>
#include <fnd/List.h>
#include <nn/hac/GameCardHeader.h>
#include "KeyConfiguration.h"
#include "types.h"
#include "KeyBag.h"
#include "PfsProcess.h"
#include "common.h"
#include <pietendo/hac/GameCardHeader.h>
namespace nstool {
class GameCardProcess
{
@ -18,60 +15,42 @@ public:
void process();
// generic
void setInputFile(const fnd::SharedPtr<fnd::IFile>& file);
void setKeyCfg(const KeyConfiguration& keycfg);
void setInputFile(const std::shared_ptr<tc::io::IStream>& file);
void setKeyCfg(const KeyBag& keycfg);
void setCliOutputMode(CliOutputMode type);
void setVerifyMode(bool verify);
// xci specific
void setPartitionForExtract(const std::string& partition_name, const std::string& extract_path);
void setListFs(bool list_fs);
// fs specific
void setShowFsTree(bool show_fs_tree);
void setExtractJobs(const std::vector<nstool::ExtractJob> extract_jobs);
private:
const std::string kModuleName = "GameCardProcess";
const std::string kXciMountPointName = "gamecard:/";
const std::string kXciMountPointName = "gamecard";
fnd::SharedPtr<fnd::IFile> mFile;
KeyConfiguration mKeyCfg;
std::string mModuleName;
std::shared_ptr<tc::io::IStream> mFile;
KeyBag mKeyCfg;
CliOutputMode mCliOutputMode;
bool mVerify;
bool mListFs;
struct sExtractInfo
{
std::string partition_name;
std::string extract_path;
void operator=(const sExtractInfo& other)
{
partition_name = other.partition_name;
extract_path = other.extract_path;
}
bool operator==(const std::string& name) const
{
return name == partition_name;
}
};
bool mIsTrueSdkXci;
bool mIsSdkXciEncrypted;
size_t mGcHeaderOffset;
bool mProccessExtendedHeader;
byte_t mHdrSignature[fnd::rsa::kRsa2048Size];
fnd::sha::sSha256Hash mHdrHash;
nn::hac::GameCardHeader mHdr;
pie::hac::detail::rsa2048_signature_t mHdrSignature;
pie::hac::detail::sha256_hash_t mHdrHash;
pie::hac::GameCardHeader mHdr;
PfsProcess mRootPfs;
fnd::List<sExtractInfo> mExtractInfo;
// fs processing
std::shared_ptr<tc::io::IFileSystem> mFileSystem;
FsProcess mFsProcess;
void importHeader();
void displayHeader();
bool validateRegionOfFile(size_t offset, size_t len, const byte_t* test_hash, bool use_salt, byte_t salt);
bool validateRegionOfFile(size_t offset, size_t len, const byte_t* test_hash);
bool validateRegionOfFile(int64_t offset, int64_t len, const byte_t* test_hash, bool use_salt, byte_t salt);
bool validateRegionOfFile(int64_t offset, int64_t len, const byte_t* test_hash);
void validateXciSignature();
void processRootPfs();
void processPartitionPfs();
};
};
}

View file

@ -1,114 +1,113 @@
#include <iostream>
#include <iomanip>
#include <fnd/io.h>
#include <fnd/SimpleFile.h>
#include <fnd/SimpleTextOutput.h>
#include <fnd/OffsetAdjustedIFile.h>
#include <fnd/Vec.h>
#include "IniProcess.h"
#include "util.h"
#include "KipProcess.h"
IniProcess::IniProcess() :
nstool::IniProcess::IniProcess() :
mModuleName("nstool::IniProcess"),
mFile(),
mCliOutputMode(_BIT(OUTPUT_BASIC)),
mCliOutputMode(true, false, false, false),
mVerify(false),
mDoExtractKip(false),
mKipExtractPath()
{
}
void IniProcess::process()
void nstool::IniProcess::process()
{
importHeader();
importKipList();
if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
if (mCliOutputMode.show_basic_info)
{
displayHeader();
displayKipList();
}
if (mDoExtractKip)
if (mKipExtractPath.isSet())
{
extractKipList();
}
}
void IniProcess::setInputFile(const fnd::SharedPtr<fnd::IFile>& file)
void nstool::IniProcess::setInputFile(const std::shared_ptr<tc::io::IStream>& file)
{
mFile = file;
}
void IniProcess::setCliOutputMode(CliOutputMode type)
void nstool::IniProcess::setCliOutputMode(CliOutputMode type)
{
mCliOutputMode = type;
}
void IniProcess::setVerifyMode(bool verify)
void nstool::IniProcess::setVerifyMode(bool verify)
{
mVerify = verify;
}
void IniProcess::setKipExtractPath(const std::string& path)
void nstool::IniProcess::setKipExtractPath(const tc::io::Path& path)
{
mDoExtractKip = true;
mKipExtractPath = path;
}
void IniProcess::importHeader()
void nstool::IniProcess::importHeader()
{
fnd::Vec<byte_t> scratch;
if (*mFile == nullptr)
if (mFile == nullptr)
{
throw fnd::Exception(kModuleName, "No file reader set.");
throw tc::Exception(mModuleName, "No file reader set.");
}
if (mFile->canRead() == false || mFile->canSeek() == false)
{
throw tc::NotSupportedException(mModuleName, "Input stream requires read/seek permissions.");
}
if ((*mFile)->size() < sizeof(nn::hac::sIniHeader))
// check if file_size is smaller than INI header size
if (tc::io::IOUtil::castInt64ToSize(mFile->length()) < sizeof(pie::hac::sIniHeader))
{
throw fnd::Exception(kModuleName, "Corrupt INI: file too small");
throw tc::Exception(mModuleName, "Corrupt INI: file too small.");
}
scratch.alloc(sizeof(nn::hac::sIniHeader));
(*mFile)->read(scratch.data(), 0, scratch.size());
// read ini
tc::ByteData scratch = tc::ByteData(sizeof(pie::hac::sIniHeader));
mFile->seek(0, tc::io::SeekOrigin::Begin);
mFile->read(scratch.data(), scratch.size());
// parse ini header
mHdr.fromBytes(scratch.data(), scratch.size());
}
void IniProcess::importKipList()
void nstool::IniProcess::importKipList()
{
// kip pos info
size_t kip_pos = sizeof(nn::hac::sIniHeader);
size_t kip_size = 0;
int64_t kip_pos = tc::io::IOUtil::castSizeToInt64(sizeof(pie::hac::sIniHeader));
int64_t kip_size = 0;
// tmp data to determine size
fnd::Vec<byte_t> hdr_raw;
nn::hac::KernelInitialProcessHeader hdr;
pie::hac::sKipHeader hdr_raw;
pie::hac::KernelInitialProcessHeader hdr;
hdr_raw.alloc(sizeof(nn::hac::sKipHeader));
for (size_t i = 0; i < mHdr.getKipNum(); i++)
{
(*mFile)->read(hdr_raw.data(), kip_pos, hdr_raw.size());
hdr.fromBytes(hdr_raw.data(), hdr_raw.size());
mFile->seek(kip_pos, tc::io::SeekOrigin::Begin);
mFile->read((byte_t*)&hdr_raw, sizeof(hdr_raw));
hdr.fromBytes((byte_t*)&hdr_raw, sizeof(hdr_raw));
kip_size = getKipSizeFromHeader(hdr);
mKipList.addElement(new fnd::OffsetAdjustedIFile(mFile, kip_pos, kip_size));
mKipList.push_back({hdr, std::make_shared<tc::io::SubStream>(tc::io::SubStream(mFile, kip_pos, kip_size))});
kip_pos += kip_size;
}
}
void IniProcess::displayHeader()
void nstool::IniProcess::displayHeader()
{
std::cout << "[INI Header]" << std::endl;
std::cout << " Size: 0x" << std::hex << mHdr.getSize() << std::endl;
std::cout << " KIP Num: " << std::dec << (uint32_t)mHdr.getKipNum() << std::endl;
fmt::print("[INI Header]\n");
fmt::print(" Size: 0x{:x}\n", mHdr.getSize());
fmt::print(" KIP Num: {:d}\n", mHdr.getKipNum());
}
void IniProcess::displayKipList()
void nstool::IniProcess::displayKipList()
{
for (size_t i = 0; i < mKipList.size(); i++)
for (auto itr = mKipList.begin(); itr != mKipList.end(); itr++)
{
KipProcess obj;
obj.setInputFile(mKipList[i]);
obj.setInputFile(itr->stream);
obj.setCliOutputMode(mCliOutputMode);
obj.setVerifyMode(mVerify);
@ -116,55 +115,33 @@ void IniProcess::displayKipList()
}
}
void IniProcess::extractKipList()
void nstool::IniProcess::extractKipList()
{
fnd::Vec<byte_t> cache;
nn::hac::KernelInitialProcessHeader hdr;
// allocate cache memory
cache.alloc(kCacheSize);
tc::ByteData cache = tc::ByteData(kCacheSize);
// make extract dir
fnd::io::makeDirectory(mKipExtractPath);
tc::io::LocalFileSystem local_fs;
local_fs.createDirectory(mKipExtractPath.get());
// outfile object for writing KIP
fnd::SimpleFile out_file;
std::string out_path;
size_t out_size;
// out path for extracted KIP
tc::io::Path out_path;
for (size_t i = 0; i < mKipList.size(); i++)
// extract KIPs
for (auto itr = mKipList.begin(); itr != mKipList.end(); itr++)
{
// read header
(*mKipList[i])->read(cache.data(), 0, cache.size());
hdr.fromBytes(cache.data(), cache.size());
out_path = mKipExtractPath.get();
out_path += fmt::format("{:s}.kip", itr->hdr.getName());
// generate path
out_path.clear();
fnd::io::appendToPath(out_path, mKipExtractPath);
fnd::io::appendToPath(out_path, hdr.getName() + kKipExtention);
if (mCliOutputMode.show_basic_info)
fmt::print("Saving {:s}...\n", out_path.to_string());
// open file
out_file.open(out_path, fnd::SimpleFile::Create);
// get kip file size
out_size = (*mKipList[i])->size();
// extract kip
if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
printf("extract=[%s]\n", out_path.c_str());
(*mKipList[i])->seek(0);
for (size_t j = 0; j < ((out_size / kCacheSize) + ((out_size % kCacheSize) != 0)); j++)
{
(*mKipList[i])->read(cache.data(), _MIN(out_size - (kCacheSize * j), kCacheSize));
out_file.write(cache.data(), _MIN(out_size - (kCacheSize * j), kCacheSize));
}
out_file.close();
writeStreamToFile(itr->stream, out_path, cache);
}
}
size_t IniProcess::getKipSizeFromHeader(const nn::hac::KernelInitialProcessHeader& hdr) const
{
return sizeof(nn::hac::sKipHeader) + hdr.getTextSegmentInfo().file_layout.size + hdr.getRoSegmentInfo().file_layout.size + hdr.getDataSegmentInfo().file_layout.size;
int64_t nstool::IniProcess::getKipSizeFromHeader(const pie::hac::KernelInitialProcessHeader& hdr) const
{
// the order of elements in a KIP are sequential, there are no file offsets
return int64_t(sizeof(pie::hac::sKipHeader)) + int64_t(hdr.getTextSegmentInfo().file_layout.size + hdr.getRoSegmentInfo().file_layout.size + hdr.getDataSegmentInfo().file_layout.size);
}

View file

@ -1,14 +1,10 @@
#pragma once
#include <vector>
#include <string>
#include <fnd/types.h>
#include <fnd/IFile.h>
#include <fnd/List.h>
#include <fnd/SharedPtr.h>
#include <nn/hac/IniHeader.h>
#include <nn/hac/KernelInitialProcessHeader.h>
#include "types.h"
#include "common.h"
#include <pietendo/hac/IniHeader.h>
#include <pietendo/hac/KernelInitialProcessHeader.h>
namespace nstool {
class IniProcess
{
@ -17,25 +13,29 @@ public:
void process();
void setInputFile(const fnd::SharedPtr<fnd::IFile>& file);
void setInputFile(const std::shared_ptr<tc::io::IStream>& file);
void setCliOutputMode(CliOutputMode type);
void setVerifyMode(bool verify);
void setKipExtractPath(const std::string& path);
void setKipExtractPath(const tc::io::Path& path);
private:
const std::string kModuleName = "IniProcess";
const std::string kKipExtention = ".kip";
const size_t kCacheSize = 0x10000;
fnd::SharedPtr<fnd::IFile> mFile;
std::string mModuleName;
std::shared_ptr<tc::io::IStream> mFile;
CliOutputMode mCliOutputMode;
bool mVerify;
bool mDoExtractKip;
std::string mKipExtractPath;
tc::Optional<tc::io::Path> mKipExtractPath;
nn::hac::IniHeader mHdr;
fnd::List<fnd::SharedPtr<fnd::IFile>> mKipList;
pie::hac::IniHeader mHdr;
struct InnerKipInfo
{
pie::hac::KernelInitialProcessHeader hdr;
std::shared_ptr<tc::io::IStream> stream;
};
std::vector<InnerKipInfo> mKipList;
void importHeader();
void importKipList();
@ -43,5 +43,7 @@ private:
void displayKipList();
void extractKipList();
size_t getKipSizeFromHeader(const nn::hac::KernelInitialProcessHeader& hdr) const;
};
int64_t getKipSizeFromHeader(const pie::hac::KernelInitialProcessHeader& hdr) const;
};
}

1042
src/KeyBag.cpp Normal file

File diff suppressed because it is too large Load diff

86
src/KeyBag.h Normal file
View file

@ -0,0 +1,86 @@
#pragma once
#include <string>
#include <vector>
#include <array>
#include <map>
#include <tc/Optional.h>
#include <tc/io.h>
#include <pietendo/hac/es/SignUtils.h>
#include <pietendo/hac/define/types.h>
#include <pietendo/hac/define/nca.h>
namespace nstool {
struct KeyBag
{
using aes128_key_t = pie::hac::detail::aes128_key_t;
using aes128_xtskey_t = pie::hac::detail::aes128_xtskey_t;
using rsa_key_t = tc::crypto::RsaKey;
//using ecc_key_t = tc::crypto::EccKey;
using rights_id_t = pie::hac::detail::rights_id_t;
using key_generation_t = byte_t;
using broadon_issuer_t = std::string;
static const size_t kNcaKeakNum = pie::hac::nca::kKeyAreaEncryptionKeyNum;
// acid
std::map<key_generation_t, rsa_key_t> acid_sign_key;
// pkg1 and pkg2
std::map<key_generation_t, aes128_key_t> pkg1_key;
std::map<key_generation_t, aes128_key_t> pkg2_key;
tc::Optional<rsa_key_t> pkg2_sign_key;
// nca
tc::Optional<aes128_xtskey_t> nca_header_key;
std::map<key_generation_t, rsa_key_t> nca_header_sign0_key;
std::array<std::map<key_generation_t, aes128_key_t>, kNcaKeakNum> nca_key_area_encryption_key;
std::array<std::map<key_generation_t, aes128_key_t>, kNcaKeakNum> nca_key_area_encryption_key_hw;
// external content keys (nca<->ticket)
std::map<rights_id_t, aes128_key_t> external_content_keys;
std::map<rights_id_t, aes128_key_t> external_enc_content_keys; // encrypted content key list to be used when external_content_keys does not have the required content key (usually taken raw from ticket)
tc::Optional<aes128_key_t> fallback_enc_content_key; // encrypted content key to be used when external_content_keys does not have the required content key (usually taken raw from ticket)
tc::Optional<aes128_key_t> fallback_content_key; // content key to be used when external_content_keys does not have the required content key (usually already decrypted from ticket)
// nrr
std::map<key_generation_t, rsa_key_t> nrr_certificate_sign_key;
// xci
tc::Optional<rsa_key_t> xci_header_sign_key;
std::map<byte_t, aes128_key_t> xci_header_key;
std::map<byte_t, aes128_key_t> xci_initial_data_kek;
tc::Optional<rsa_key_t> xci_cert_sign_key;
// ticket
std::map<key_generation_t, aes128_key_t> etik_common_key;
// BroadOn signer profiles (for es cert and es tik)
// BroadOn Keys
struct BroadOnSignerProfile
{
tc::ByteData certificate;
pie::hac::es::sign::SignatureAlgo key_type;
rsa_key_t rsa_key;
// ecc_key_t ecc_key;
};
std::map<broadon_issuer_t, BroadOnSignerProfile> broadon_signer;
};
class KeyBagInitializer : public KeyBag
{
public:
KeyBagInitializer(bool isDev, const tc::Optional<tc::io::Path>& keyfile_path, const tc::Optional<tc::io::Path>& titlekeyfile_path, const std::vector<tc::io::Path>& tik_path_list, const tc::Optional<tc::io::Path>& cert_path);
private:
KeyBagInitializer();
void importBaseKeyFile(const tc::io::Path& keyfile_path, bool isDev);
void importTitleKeyFile(const tc::io::Path& keyfile_path);
void importCertificateChain(const tc::io::Path& cert_path);
void importTicket(const tc::io::Path& tik_path);
void importKnownKeys(bool isDev);
};
}

View file

@ -1,413 +0,0 @@
#include "KeyConfiguration.h"
#include <fnd/ResourceFileReader.h>
#include <fnd/SimpleTextOutput.h>
#include <nn/hac/AesKeygen.h>
KeyConfiguration::KeyConfiguration()
{
clearGeneralKeyConfiguration();
clearNcaExternalKeys();
}
KeyConfiguration::KeyConfiguration(const KeyConfiguration& other)
{
*this = other;
}
void KeyConfiguration::operator=(const KeyConfiguration& other)
{
mPkg2SignKey = other.mPkg2SignKey;
mXciHeaderSignKey = other.mXciHeaderSignKey;
mContentArchiveHeaderKey = other.mContentArchiveHeaderKey;
mXciHeaderKey = other.mXciHeaderKey;
for (size_t i = 0; i < kMasterKeyNum; i++)
{
mAcidSignKey[i] = other.mAcidSignKey[i];
mContentArchiveHeader0SignKey[i] = other.mContentArchiveHeader0SignKey[i];
mNrrCertificateSignKey[i] = other.mNrrCertificateSignKey[i];
mPkg2Key[i] = other.mPkg2Key[i];
mPkg1Key[i] = other.mPkg1Key[i];
mNcaKeyAreaEncryptionKey[0][i] = other.mNcaKeyAreaEncryptionKey[0][i];
mNcaKeyAreaEncryptionKey[1][i] = other.mNcaKeyAreaEncryptionKey[1][i];
mNcaKeyAreaEncryptionKey[2][i] = other.mNcaKeyAreaEncryptionKey[2][i];
mNcaKeyAreaEncryptionKeyHw[0][i] = other.mNcaKeyAreaEncryptionKeyHw[0][i];
mNcaKeyAreaEncryptionKeyHw[1][i] = other.mNcaKeyAreaEncryptionKeyHw[1][i];
mNcaKeyAreaEncryptionKeyHw[2][i] = other.mNcaKeyAreaEncryptionKeyHw[2][i];
mETicketCommonKey[i] = other.mETicketCommonKey[i];
}
mPkiRootKeyList = other.mPkiRootKeyList;
mNcaExternalContentKeyList = other.mNcaExternalContentKeyList;
}
void KeyConfiguration::importHactoolGenericKeyfile(const std::string& path)
{
clearGeneralKeyConfiguration();
fnd::ResourceFileReader res;
try
{
res.processFile(path);
}
catch (const fnd::Exception&)
{
throw fnd::Exception(kModuleName, "Failed to open key file: " + path);
}
// internally used sources
fnd::aes::sAes128Key master_key[kMasterKeyNum] = { kNullAesKey };
fnd::aes::sAes128Key package2_key_source = kNullAesKey;
fnd::aes::sAes128Key ticket_titlekek_source = kNullAesKey;
fnd::aes::sAes128Key key_area_key_source[kNcaKeakNum] = { kNullAesKey, kNullAesKey, kNullAesKey };
fnd::aes::sAes128Key aes_kek_generation_source = kNullAesKey;
fnd::aes::sAes128Key aes_key_generation_source = kNullAesKey;
fnd::aes::sAes128Key nca_header_kek_source = kNullAesKey;
fnd::aes::sAesXts128Key nca_header_key_source = kNullAesXtsKey;
fnd::rsa::sRsa4096Key pki_root_sign_key = kNullRsa4096Key;
#define _CONCAT_2_STRINGS(str1, str2) ((str1) + "_" + (str2))
#define _CONCAT_3_STRINGS(str1, str2, str3) _CONCAT_2_STRINGS(_CONCAT_2_STRINGS(str1, str2), str3)
#define _CONCAT_4_STRINGS(str1, str2, str3, str4) _CONCAT_2_STRINGS(_CONCAT_2_STRINGS(_CONCAT_2_STRINGS(str1, str2), str3), str4)
std::string key,val;
fnd::Vec<byte_t> dec_array;
#define _SAVE_KEYDATA(key_name, array, len) \
key = (key_name); \
val = res[key]; \
if (val.empty() == false) { \
fnd::SimpleTextOutput::stringToArray(val, dec_array); \
if (dec_array.size() != len) \
throw fnd::Exception(kModuleName, "Key: \"" + key_name + "\" has incorrect length"); \
memcpy(array, dec_array.data(), len); \
}
for (size_t nameidx = 0; nameidx < kNameVariantNum; nameidx++)
{
// import sources
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kPkg2Base[nameidx], kKeyStr, kSourceStr), package2_key_source.key, 0x10);
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kTicketCommonKeyBase[nameidx], kSourceStr), ticket_titlekek_source.key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaKeyAreaEncKeyBase[nameidx], kNcaKeyAreaKeyIndexStr[0], kSourceStr), key_area_key_source[0].key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaKeyAreaEncKeyBase[nameidx], kNcaKeyAreaKeyIndexStr[1], kSourceStr), key_area_key_source[1].key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaKeyAreaEncKeyBase[nameidx], kNcaKeyAreaKeyIndexStr[2], kSourceStr), key_area_key_source[2].key, 0x10);
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kKekGenBase[nameidx], kSourceStr), aes_kek_generation_source.key, 0x10);
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kKeyGenBase[nameidx], kSourceStr), aes_key_generation_source.key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kContentArchiveHeaderBase[nameidx], kKekStr, kSourceStr), nca_header_kek_source.key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kContentArchiveHeaderBase[nameidx], kKeyStr, kSourceStr), nca_header_key_source.key, 0x20);
// Store Key Variants/Derivatives
for (size_t mkeyidx = 0; mkeyidx < kMasterKeyNum; mkeyidx++)
{
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kMasterBase[nameidx], kKeyStr, kKeyIndex[mkeyidx]), master_key[mkeyidx].key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kPkg1Base[nameidx], kKeyStr, kKeyIndex[mkeyidx]), mPkg1Key[mkeyidx].key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kPkg2Base[nameidx], kKeyStr, kKeyIndex[mkeyidx]), mPkg2Key[mkeyidx].key, 0x10);
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kTicketCommonKeyBase[nameidx], kKeyIndex[mkeyidx]), mETicketCommonKey[mkeyidx].key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaKeyAreaEncKeyBase[nameidx], kNcaKeyAreaKeyIndexStr[0], kKeyIndex[mkeyidx]), mNcaKeyAreaEncryptionKey[0][mkeyidx].key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaKeyAreaEncKeyBase[nameidx], kNcaKeyAreaKeyIndexStr[1], kKeyIndex[mkeyidx]), mNcaKeyAreaEncryptionKey[1][mkeyidx].key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaKeyAreaEncKeyBase[nameidx], kNcaKeyAreaKeyIndexStr[2], kKeyIndex[mkeyidx]), mNcaKeyAreaEncryptionKey[2][mkeyidx].key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaKeyAreaEncKeyHwBase[nameidx], kNcaKeyAreaKeyIndexStr[0], kKeyIndex[mkeyidx]), mNcaKeyAreaEncryptionKeyHw[0][mkeyidx].key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaKeyAreaEncKeyHwBase[nameidx], kNcaKeyAreaKeyIndexStr[1], kKeyIndex[mkeyidx]), mNcaKeyAreaEncryptionKeyHw[1][mkeyidx].key, 0x10);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kNcaKeyAreaEncKeyHwBase[nameidx], kNcaKeyAreaKeyIndexStr[2], kKeyIndex[mkeyidx]), mNcaKeyAreaEncryptionKeyHw[2][mkeyidx].key, 0x10);
_SAVE_KEYDATA(_CONCAT_4_STRINGS(kContentArchiveHeaderBase[nameidx], kSignKey, kKeyIndex[mkeyidx], kPrivateStr), mContentArchiveHeader0SignKey[mkeyidx].priv_exponent, fnd::rsa::kRsa2048Size);
_SAVE_KEYDATA(_CONCAT_4_STRINGS(kContentArchiveHeaderBase[nameidx], kSignKey, kKeyIndex[mkeyidx], kModulusStr), mContentArchiveHeader0SignKey[mkeyidx].modulus, fnd::rsa::kRsa2048Size);
_SAVE_KEYDATA(_CONCAT_4_STRINGS(kAcidBase[nameidx], kSignKey, kKeyIndex[mkeyidx], kPrivateStr), mAcidSignKey[mkeyidx].priv_exponent, fnd::rsa::kRsa2048Size);
_SAVE_KEYDATA(_CONCAT_4_STRINGS(kAcidBase[nameidx], kSignKey, kKeyIndex[mkeyidx], kModulusStr), mAcidSignKey[mkeyidx].modulus, fnd::rsa::kRsa2048Size);
_SAVE_KEYDATA(_CONCAT_4_STRINGS(kNrrCertBase[nameidx], kSignKey, kKeyIndex[mkeyidx], kPrivateStr), mNrrCertificateSignKey[mkeyidx].priv_exponent, fnd::rsa::kRsa2048Size);
_SAVE_KEYDATA(_CONCAT_4_STRINGS(kNrrCertBase[nameidx], kSignKey, kKeyIndex[mkeyidx], kModulusStr), mNrrCertificateSignKey[mkeyidx].modulus, fnd::rsa::kRsa2048Size);
}
// store nca header key
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kContentArchiveHeaderBase[nameidx], kKeyStr), mContentArchiveHeaderKey.key[0], 0x20);
// store xci header key
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kXciHeaderBase[nameidx], kKeyStr), mXciHeaderKey.key, 0x10);
// store rsa keys
// legacy header nca key name
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kContentArchiveHeaderBase[nameidx], kSignKey, kPrivateStr), mContentArchiveHeader0SignKey[0].priv_exponent, fnd::rsa::kRsa2048Size);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kContentArchiveHeaderBase[nameidx], kSignKey, kModulusStr), mContentArchiveHeader0SignKey[0].modulus, fnd::rsa::kRsa2048Size);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kXciHeaderBase[nameidx], kSignKey, kPrivateStr), mXciHeaderSignKey.priv_exponent, fnd::rsa::kRsa2048Size);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kXciHeaderBase[nameidx], kSignKey, kModulusStr), mXciHeaderSignKey.modulus, fnd::rsa::kRsa2048Size);
// legacy acid header key name
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kAcidBase[nameidx], kSignKey, kPrivateStr), mAcidSignKey[0].priv_exponent, fnd::rsa::kRsa2048Size);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kAcidBase[nameidx], kSignKey, kModulusStr), mAcidSignKey[0].modulus, fnd::rsa::kRsa2048Size);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kPkg2Base[nameidx], kSignKey, kPrivateStr), mPkg2SignKey.priv_exponent, fnd::rsa::kRsa2048Size);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kPkg2Base[nameidx], kSignKey, kModulusStr), mPkg2SignKey.modulus, fnd::rsa::kRsa2048Size);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kPkiRootBase[nameidx], kSignKey, kPrivateStr), pki_root_sign_key.priv_exponent, fnd::rsa::kRsa4096Size);
_SAVE_KEYDATA(_CONCAT_3_STRINGS(kPkiRootBase[nameidx], kSignKey, kModulusStr), pki_root_sign_key.modulus, fnd::rsa::kRsa4096Size);
}
#undef _SAVE_KEYDATA
#undef _CONCAT_3_STRINGS
#undef _CONCAT_2_STRINGS
// Derive keys
for (size_t i = 0; i < kMasterKeyNum; i++)
{
if (master_key[i] != kNullAesKey)
{
if (aes_kek_generation_source != kNullAesKey && aes_key_generation_source != kNullAesKey)
{
if (i == 0 && nca_header_kek_source != kNullAesKey && nca_header_key_source != kNullAesXtsKey)
{
if (mContentArchiveHeaderKey == kNullAesXtsKey)
{
fnd::aes::sAes128Key nca_header_kek;
nn::hac::AesKeygen::generateKey(nca_header_kek.key, aes_kek_generation_source.key, nca_header_kek_source.key, aes_key_generation_source.key, master_key[i].key);
nn::hac::AesKeygen::generateKey(mContentArchiveHeaderKey.key[0], nca_header_key_source.key[0], nca_header_kek.key);
nn::hac::AesKeygen::generateKey(mContentArchiveHeaderKey.key[1], nca_header_key_source.key[1], nca_header_kek.key);
}
}
for (size_t j = 0; j < nn::hac::nca::kKeyAreaEncryptionKeyNum; j++)
{
if (key_area_key_source[j] != kNullAesKey && mNcaKeyAreaEncryptionKey[j][i] == kNullAesKey)
{
nn::hac::AesKeygen::generateKey(mNcaKeyAreaEncryptionKey[j][i].key, aes_kek_generation_source.key, key_area_key_source[j].key, aes_key_generation_source.key, master_key[i].key);
}
}
}
if (ticket_titlekek_source != kNullAesKey && mETicketCommonKey[i] == kNullAesKey)
{
nn::hac::AesKeygen::generateKey(mETicketCommonKey[i].key, ticket_titlekek_source.key, master_key[i].key);
}
if (package2_key_source != kNullAesKey && mPkg2Key[i] == kNullAesKey)
{
nn::hac::AesKeygen::generateKey(mPkg2Key[i].key, package2_key_source.key, master_key[i].key);
}
}
}
// populate pki root keys
if (pki_root_sign_key != kNullRsa4096Key)
{
sPkiRootKey tmp;
tmp.name = nn::pki::sign::kRootIssuerStr;
tmp.key_type = nn::pki::sign::SIGN_ALGO_RSA4096;
tmp.rsa4096_key = pki_root_sign_key;
mPkiRootKeyList.addElement(tmp);
}
}
void KeyConfiguration::clearGeneralKeyConfiguration()
{
mPkg2SignKey = kNullRsa2048Key;
mXciHeaderSignKey = kNullRsa2048Key;
mPkiRootKeyList.clear();
mContentArchiveHeaderKey = kNullAesXtsKey;
mXciHeaderKey = kNullAesKey;
for (size_t i = 0; i < kMasterKeyNum; i++)
{
mAcidSignKey[i] = kNullRsa2048Key;
mContentArchiveHeader0SignKey[i] = kNullRsa2048Key;
mNrrCertificateSignKey[i] = kNullRsa2048Key;
mPkg1Key[i] = kNullAesKey;
mPkg2Key[i] = kNullAesKey;
mETicketCommonKey[i] = kNullAesKey;
for (size_t j = 0; j < kNcaKeakNum; j++)
{
mNcaKeyAreaEncryptionKey[j][i] = kNullAesKey;
mNcaKeyAreaEncryptionKeyHw[j][i] = kNullAesKey;
}
}
}
void KeyConfiguration::clearNcaExternalKeys()
{
mNcaExternalContentKeyList.clear();
}
bool KeyConfiguration::getContentArchiveHeaderKey(fnd::aes::sAesXts128Key& key) const
{
return copyOutKeyResourceIfExists(mContentArchiveHeaderKey, key, kNullAesXtsKey);
}
bool KeyConfiguration::getContentArchiveHeader0SignKey(fnd::rsa::sRsa2048Key& key, byte_t key_generation) const
{
// TODO: This needs to be changed to support multiple keys
if (key_generation >= kMasterKeyNum)
{
return false;
}
return copyOutKeyResourceIfExists(mContentArchiveHeader0SignKey[key_generation], key, kNullRsa2048Key);
}
bool KeyConfiguration::getAcidSignKey(fnd::rsa::sRsa2048Key& key, byte_t key_generation) const
{
// TODO: This needs to be changed to support multiple keys
if (key_generation >= kMasterKeyNum)
{
return false;
}
return copyOutKeyResourceIfExists(mAcidSignKey[key_generation], key, kNullRsa2048Key);
}
bool KeyConfiguration::getNcaKeyAreaEncryptionKey(byte_t masterkey_index, byte_t keak_type, fnd::aes::sAes128Key& key) const
{
if (keak_type >= kNcaKeakNum || masterkey_index >= kMasterKeyNum)
{
return false;
}
return copyOutKeyResourceIfExists(mNcaKeyAreaEncryptionKey[keak_type][masterkey_index], key, kNullAesKey);
}
bool KeyConfiguration::getNcaKeyAreaEncryptionKeyHw(byte_t masterkey_index, byte_t keak_type, fnd::aes::sAes128Key& key) const
{
if (keak_type >= kNcaKeakNum || masterkey_index >= kMasterKeyNum)
{
return false;
}
return copyOutKeyResourceIfExists(mNcaKeyAreaEncryptionKeyHw[keak_type][masterkey_index], key, kNullAesKey);
}
void KeyConfiguration::addNcaExternalContentKey(const byte_t rights_id[nn::hac::nca::kRightsIdLen], const fnd::aes::sAes128Key& key)
{
sNcaExternalContentKey tmp;
memcpy(tmp.rights_id.data, rights_id, nn::hac::nca::kRightsIdLen);
tmp.key = key;
if (mNcaExternalContentKeyList.hasElement(tmp))
return;
mNcaExternalContentKeyList.addElement(tmp);
}
bool KeyConfiguration::getNcaExternalContentKey(const byte_t rights_id[nn::hac::nca::kRightsIdLen], fnd::aes::sAes128Key& key) const
{
sRightsId id;
bool res_exists = false;
memcpy(id.data, rights_id, nn::hac::nca::kRightsIdLen);
for (size_t i = 0; i < mNcaExternalContentKeyList.size(); i++)
{
if (mNcaExternalContentKeyList[i].rights_id == id)
{
res_exists = true;
key = mNcaExternalContentKeyList[i].key;
break;
}
}
return res_exists;
}
bool KeyConfiguration::getNrrCertificateSignKey(fnd::rsa::sRsa2048Key& key, byte_t key_generation) const
{
// TODO: This needs to be changed to support multiple keys
if (key_generation >= kMasterKeyNum)
{
return false;
}
return copyOutKeyResourceIfExists(mNrrCertificateSignKey[key_generation], key, kNullRsa2048Key);
}
bool KeyConfiguration::getPkg1Key(byte_t masterkey_index, fnd::aes::sAes128Key& key) const
{
if (masterkey_index >= kMasterKeyNum)
{
return false;
}
return copyOutKeyResourceIfExists(mPkg1Key[masterkey_index], key, kNullAesKey);
}
bool KeyConfiguration::getPkg2Key(byte_t masterkey_index, fnd::aes::sAes128Key& key) const
{
if (masterkey_index >= kMasterKeyNum)
{
return false;
}
return copyOutKeyResourceIfExists(mPkg2Key[masterkey_index], key, kNullAesKey);
}
bool KeyConfiguration::getPkg2SignKey(fnd::rsa::sRsa2048Key& key) const
{
return copyOutKeyResourceIfExists(mPkg2SignKey, key, kNullRsa2048Key);
}
bool KeyConfiguration::getXciHeaderSignKey(fnd::rsa::sRsa2048Key& key) const
{
return copyOutKeyResourceIfExists(mXciHeaderSignKey, key, kNullRsa2048Key);
}
bool KeyConfiguration::getXciHeaderKey(fnd::aes::sAes128Key& key) const
{
return copyOutKeyResourceIfExists(mXciHeaderKey, key, kNullAesKey);
}
bool KeyConfiguration::getETicketCommonKey(byte_t masterkey_index, fnd::aes::sAes128Key& key) const
{
if (masterkey_index >= kMasterKeyNum)
{
return false;
}
return copyOutKeyResourceIfExists(mETicketCommonKey[masterkey_index], key, kNullAesKey);
}
bool KeyConfiguration::getPkiRootSignKey(const std::string& root_name, fnd::rsa::sRsa4096Key& key) const
{
bool res_exists = false;
for (size_t i = 0; i < mPkiRootKeyList.size(); i++)
{
if (root_name == mPkiRootKeyList[i].name && mPkiRootKeyList[i].key_type == nn::pki::sign::SIGN_ALGO_RSA4096)
{
res_exists = true;
key = mPkiRootKeyList[i].rsa4096_key;
break;
}
}
return res_exists;
}
bool KeyConfiguration::getPkiRootSignKey(const std::string& root_name, fnd::rsa::sRsa2048Key& key) const
{
bool res_exists = false;
for (size_t i = 0; i < mPkiRootKeyList.size(); i++)
{
if (root_name == mPkiRootKeyList[i].name && mPkiRootKeyList[i].key_type == nn::pki::sign::SIGN_ALGO_RSA2048)
{
res_exists = true;
key = mPkiRootKeyList[i].rsa2048_key;
break;
}
}
return res_exists;
}
bool KeyConfiguration::getPkiRootSignKey(const std::string& root_name, fnd::ecdsa::sEcdsa240Key& key) const
{
bool res_exists = false;
for (size_t i = 0; i < mPkiRootKeyList.size(); i++)
{
if (root_name == mPkiRootKeyList[i].name && mPkiRootKeyList[i].key_type == nn::pki::sign::SIGN_ALGO_ECDSA240)
{
res_exists = true;
key = mPkiRootKeyList[i].ecdsa240_key;
break;
}
}
return res_exists;
}

View file

@ -1,217 +0,0 @@
#pragma once
#include <string>
#include <cstring>
#include <fnd/types.h>
#include <fnd/aes.h>
#include <fnd/rsa.h>
#include <fnd/ecdsa.h>
#include <nn/hac/define/nca.h>
#include <nn/pki/SignedData.h>
#include <nn/es/TicketBody_V2.h>
class KeyConfiguration
{
public:
KeyConfiguration();
KeyConfiguration(const KeyConfiguration& other);
void operator=(const KeyConfiguration& other);
void importHactoolGenericKeyfile(const std::string& path);
//void importHactoolTitleKeyfile(const std::string& path);
void clearGeneralKeyConfiguration();
void clearNcaExternalKeys();
// nca keys
bool getContentArchiveHeaderKey(fnd::aes::sAesXts128Key& key) const;
bool getContentArchiveHeader0SignKey(fnd::rsa::sRsa2048Key& key, byte_t key_generation) const;
bool getAcidSignKey(fnd::rsa::sRsa2048Key& key, byte_t key_generation) const;
bool getNcaKeyAreaEncryptionKey(byte_t masterkey_index, byte_t keak_type, fnd::aes::sAes128Key& key) const;
bool getNcaKeyAreaEncryptionKeyHw(byte_t masterkey_index, byte_t keak_type, fnd::aes::sAes128Key& key) const;
// external content keys
void addNcaExternalContentKey(const byte_t rights_id[nn::hac::nca::kRightsIdLen], const fnd::aes::sAes128Key& key);
bool getNcaExternalContentKey(const byte_t rights_id[nn::hac::nca::kRightsIdLen], fnd::aes::sAes128Key& key) const;
// nrr key
bool getNrrCertificateSignKey(fnd::rsa::sRsa2048Key& key, byte_t key_generation) const;
// pkg1/pkg2
bool getPkg1Key(byte_t masterkey_index, fnd::aes::sAes128Key& key) const;
bool getPkg2Key(byte_t masterkey_index, fnd::aes::sAes128Key& key) const;
bool getPkg2SignKey(fnd::rsa::sRsa2048Key& key) const;
// xci keys
bool getXciHeaderSignKey(fnd::rsa::sRsa2048Key& key) const;
bool getXciHeaderKey(fnd::aes::sAes128Key& key) const;
// ticket
bool getETicketCommonKey(byte_t masterkey_index, fnd::aes::sAes128Key& key) const;
// pki
bool getPkiRootSignKey(const std::string& root_name, fnd::rsa::sRsa4096Key& key) const;
bool getPkiRootSignKey(const std::string& root_name, fnd::rsa::sRsa2048Key& key) const;
bool getPkiRootSignKey(const std::string& root_name, fnd::ecdsa::sEcdsa240Key& key) const;
private:
const std::string kModuleName = "KeyConfiguration";
const fnd::aes::sAes128Key kNullAesKey = {{0}};
const fnd::aes::sAesXts128Key kNullAesXtsKey = {{{0}}};
const fnd::rsa::sRsa4096Key kNullRsa4096Key = {{0}, {0}, {0}};
const fnd::rsa::sRsa2048Key kNullRsa2048Key = {{0}, {0}, {0}};
static const size_t kMasterKeyNum = 0x20;
static const size_t kNcaKeakNum = nn::hac::nca::kKeyAreaEncryptionKeyNum;
// keynames
enum NameVariantIndex
{
NNTOOLS,
LEGACY_HACTOOL,
LEGACY_0
};
static const size_t kNameVariantNum = 3;
const std::string kMasterBase[kNameVariantNum] = { "master", "master", "master" };
const std::string kPkg1Base[kNameVariantNum] = { "package1", "package1", "package1" };
const std::string kPkg2Base[kNameVariantNum] = { "package2", "package2", "package2" };
const std::string kXciHeaderBase[kNameVariantNum] = { "xci_header", "xci_header", "xci_header" };
const std::string kContentArchiveHeaderBase[kNameVariantNum] = { "nca_header", "header", "nca_header" };
const std::string kAcidBase[kNameVariantNum] = { "acid", "acid", "acid" };
const std::string kNrrCertBase[kNameVariantNum] = { "nrr_certificate", "nrr_certificate", "nrr_certificate" };
const std::string kPkiRootBase[kNameVariantNum] = { "pki_root", "pki_root", "pki_root" };
const std::string kTicketCommonKeyBase[kNameVariantNum] = { "ticket_commonkey", "titlekek", "ticket_commonkey" };
const std::string kNcaKeyAreaEncKeyBase[kNameVariantNum] = { "nca_key_area_key", "key_area_key", "nca_body_keak" };
const std::string kNcaKeyAreaEncKeyHwBase[kNameVariantNum] = { "nca_key_area_key_hw", "key_area_hw_key", "nca_key_area_key_hw" };
const std::string kKekGenBase[kNameVariantNum] = { "aes_kek_generation", "aes_kek_generation", "aes_kek_generation" };
const std::string kKeyGenBase[kNameVariantNum] = { "aes_key_generation", "aes_key_generation", "aes_key_generation" };
// misc str
const std::string kKeyStr = "key";
const std::string kKekStr = "kek";
const std::string kSourceStr = "source";
const std::string kSignKey = "sign_key";
const std::string kModulusStr = "modulus";
const std::string kPrivateStr = "private";
const std::string kNcaKeyAreaKeyIndexStr[kNcaKeakNum] = { "application", "ocean", "system" };
const std::string kKeyIndex[kMasterKeyNum] = {"00","01","02","03","04","05","06","07","08","09","0a","0b","0c","0d","0e","0f","10","11","12","13","14","15","16","17","18","19","1a","1b","1c","1d","1e","1f"};
struct sRightsId
{
byte_t data[nn::hac::nca::kRightsIdLen];
void operator=(const sRightsId& other)
{
memcpy(this->data, other.data, nn::hac::nca::kRightsIdLen);
}
bool operator==(const sRightsId& other) const
{
return memcmp(this->data, other.data, nn::hac::nca::kRightsIdLen) == 0;
}
bool operator!=(const sRightsId& other) const
{
return !(operator==(other));
}
};
struct sNcaExternalContentKey
{
sRightsId rights_id;
fnd::aes::sAes128Key key;
void operator=(const sNcaExternalContentKey& other)
{
rights_id = other.rights_id;
key = other.key;
}
bool operator==(const sNcaExternalContentKey& other) const
{
return (rights_id == other.rights_id) \
&& (key == other.key);
}
bool operator!=(const sNcaExternalContentKey& other) const
{
return !(operator==(other));
}
};
struct sPkiRootKey
{
std::string name;
nn::pki::sign::SignatureAlgo key_type;
fnd::rsa::sRsa4096Key rsa4096_key;
fnd::rsa::sRsa2048Key rsa2048_key;
fnd::ecdsa::sEcdsa240Key ecdsa240_key;
void operator=(const sPkiRootKey& other)
{
name = other.name;
key_type = other.key_type;
rsa4096_key = other.rsa4096_key;
rsa2048_key = other.rsa2048_key;
ecdsa240_key = other.ecdsa240_key;
}
bool operator==(const sPkiRootKey& other) const
{
return (name == other.name) \
&& (key_type == other.key_type) \
&& (rsa4096_key == other.rsa4096_key) \
&& (rsa2048_key == other.rsa2048_key) \
&& (ecdsa240_key == other.ecdsa240_key);
}
bool operator!=(const sPkiRootKey& other) const
{
return !(operator==(other));
}
};
/* general key config */
// acid
fnd::rsa::sRsa2048Key mAcidSignKey[kMasterKeyNum];
// pkg1 and pkg2
fnd::aes::sAes128Key mPkg1Key[kMasterKeyNum];
fnd::rsa::sRsa2048Key mPkg2SignKey;
fnd::aes::sAes128Key mPkg2Key[kMasterKeyNum];
// nca
fnd::rsa::sRsa2048Key mContentArchiveHeader0SignKey[kMasterKeyNum];
fnd::aes::sAesXts128Key mContentArchiveHeaderKey;
fnd::aes::sAes128Key mNcaKeyAreaEncryptionKey[kNcaKeakNum][kMasterKeyNum];
fnd::aes::sAes128Key mNcaKeyAreaEncryptionKeyHw[kNcaKeakNum][kMasterKeyNum];
// nrr
fnd::rsa::sRsa2048Key mNrrCertificateSignKey[kMasterKeyNum];
// xci
fnd::rsa::sRsa2048Key mXciHeaderSignKey;
fnd::aes::sAes128Key mXciHeaderKey;
// ticket
fnd::aes::sAes128Key mETicketCommonKey[kMasterKeyNum];
// pki
fnd::List<sPkiRootKey> mPkiRootKeyList;
/* Nca External Keys */
fnd::List<sNcaExternalContentKey> mNcaExternalContentKeyList;
template <class T>
bool copyOutKeyResourceIfExists(const T& src, T& dst, const T& null_sample) const
{
bool resource_exists = false;
if (src != null_sample)
{
resource_exists = true;
dst = src;
}
return resource_exists;
}
};

View file

@ -1,266 +1,273 @@
#include "KipProcess.h"
#include <iostream>
#include <iomanip>
#include <pietendo/hac/KernelCapabilityUtil.h>
#include <fnd/SimpleTextOutput.h>
#include <fnd/OffsetAdjustedIFile.h>
#include <fnd/Vec.h>
#include <tc/NotImplementedException.h>
#include <nn/hac/KernelCapabilityUtil.h>
KipProcess::KipProcess():
nstool::KipProcess::KipProcess() :
mModuleName("nstool::KipProcess"),
mFile(),
mCliOutputMode(_BIT(OUTPUT_BASIC)),
mCliOutputMode(true, false, false, false),
mVerify(false)
{
}
void KipProcess::process()
void nstool::KipProcess::process()
{
importHeader();
//importCodeSegments();
if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
//importCodeSegments(); // code segments not imported because compression not supported yet
if (mCliOutputMode.show_basic_info)
{
displayHeader();
displayKernelCap(mHdr.getKernelCapabilities());
}
}
void KipProcess::setInputFile(const fnd::SharedPtr<fnd::IFile>& file)
void nstool::KipProcess::setInputFile(const std::shared_ptr<tc::io::IStream>& file)
{
mFile = file;
}
void KipProcess::setCliOutputMode(CliOutputMode type)
void nstool::KipProcess::setCliOutputMode(CliOutputMode type)
{
mCliOutputMode = type;
}
void KipProcess::setVerifyMode(bool verify)
void nstool::KipProcess::setVerifyMode(bool verify)
{
mVerify = verify;
}
void KipProcess::importHeader()
void nstool::KipProcess::importHeader()
{
fnd::Vec<byte_t> scratch;
if (*mFile == nullptr)
if (mFile == nullptr)
{
throw fnd::Exception(kModuleName, "No file reader set.");
throw tc::Exception(mModuleName, "No file reader set.");
}
if (mFile->canRead() == false || mFile->canSeek() == false)
{
throw tc::NotSupportedException(mModuleName, "Input stream requires read/seek permissions.");
}
if ((*mFile)->size() < sizeof(nn::hac::sKipHeader))
// check if file_size is smaller than KIP header size
if (tc::io::IOUtil::castInt64ToSize(mFile->length()) < sizeof(pie::hac::sKipHeader))
{
throw fnd::Exception(kModuleName, "Corrupt KIP: file too small");
throw tc::Exception(mModuleName, "Corrupt KIP: file too small.");
}
scratch.alloc(sizeof(nn::hac::sKipHeader));
(*mFile)->read(scratch.data(), 0, scratch.size());
// read kip
tc::ByteData scratch = tc::ByteData(sizeof(pie::hac::sKipHeader));
mFile->seek(0, tc::io::SeekOrigin::Begin);
mFile->read(scratch.data(), scratch.size());
// parse kip header
mHdr.fromBytes(scratch.data(), scratch.size());
}
void KipProcess::importCodeSegments()
void nstool::KipProcess::importCodeSegments()
{
#ifdef _KIP_COMPRESSION_IMPLEMENTED
fnd::Vec<byte_t> scratch;
uint32_t decompressed_len;
#endif
tc::ByteData scratch;
// process text segment
#ifdef _KIP_COMPRESSION_IMPLEMENTED
if (mHdr.getTextSegmentInfo().is_compressed)
{
scratch.alloc(mHdr.getTextSegmentInfo().file_layout.size);
(*mFile)->read(scratch.data(), mHdr.getTextSegmentInfo().file_layout.offset, scratch.size());
mTextBlob.alloc(mHdr.getTextSegmentInfo().memory_layout.size);
fnd::lz4::decompressData(scratch.data(), (uint32_t)scratch.size(), mTextBlob.data(), (uint32_t)mTextBlob.size(), decompressed_len);
if (decompressed_len != mTextBlob.size())
// allocate/read compressed text
scratch = tc::ByteData(mHdr.getTextSegmentInfo().file_layout.size);
mFile->seek(mHdr.getTextSegmentInfo().file_layout.offset, tc::io::SeekOrigin::Begin);
mFile->read(scratch.data(), scratch.size());
// allocate for decompressed text segment
mTextBlob = tc::ByteData(mHdr.getTextSegmentInfo().memory_layout.size);
// decompress text segment
if (decompressData(scratch.data(), scratch.size(), mTextBlob.data(), mTextBlob.size()) != mTextBlob.size())
{
throw fnd::Exception(kModuleName, "KIP text segment failed to decompress");
throw tc::Exception(mModuleName, "KIP text segment failed to decompress");
}
}
else
{
mTextBlob.alloc(mHdr.getTextSegmentInfo().file_layout.size);
(*mFile)->read(mTextBlob.data(), mHdr.getTextSegmentInfo().file_layout.offset, mTextBlob.size());
// read text segment directly (not compressed)
mTextBlob = tc::ByteData(mHdr.getTextSegmentInfo().file_layout.size);
mFile->seek(mHdr.getTextSegmentInfo().file_layout.offset, tc::io::SeekOrigin::Begin);
mFile->read(mTextBlob.data(), mTextBlob.size());
}
#else
mTextBlob.alloc(mHdr.getTextSegmentInfo().file_layout.size);
(*mFile)->read(mTextBlob.data(), mHdr.getTextSegmentInfo().file_layout.offset, mTextBlob.size());
#endif
// process ro segment
#ifdef _KIP_COMPRESSION_IMPLEMENTED
if (mHdr.getRoSegmentInfo().is_compressed)
{
scratch.alloc(mHdr.getRoSegmentInfo().file_layout.size);
(*mFile)->read(scratch.data(), mHdr.getRoSegmentInfo().file_layout.offset, scratch.size());
mRoBlob.alloc(mHdr.getRoSegmentInfo().memory_layout.size);
fnd::lz4::decompressData(scratch.data(), (uint32_t)scratch.size(), mRoBlob.data(), (uint32_t)mRoBlob.size(), decompressed_len);
if (decompressed_len != mRoBlob.size())
// allocate/read compressed ro segment
scratch = tc::ByteData(mHdr.getRoSegmentInfo().file_layout.size);
mFile->seek(mHdr.getRoSegmentInfo().file_layout.offset, tc::io::SeekOrigin::Begin);
mFile->read(scratch.data(), scratch.size());
// allocate for decompressed ro segment
mRoBlob = tc::ByteData(mHdr.getRoSegmentInfo().memory_layout.size);
// decompress ro segment
if (decompressData(scratch.data(), scratch.size(), mRoBlob.data(), mRoBlob.size()) != mRoBlob.size())
{
throw fnd::Exception(kModuleName, "KIP ro segment failed to decompress");
throw tc::Exception(mModuleName, "KIP ro segment failed to decompress");
}
}
else
{
mRoBlob.alloc(mHdr.getRoSegmentInfo().file_layout.size);
(*mFile)->read(mRoBlob.data(), mHdr.getRoSegmentInfo().file_layout.offset, mRoBlob.size());
// read ro segment directly (not compressed)
mRoBlob = tc::ByteData(mHdr.getRoSegmentInfo().file_layout.size);
mFile->seek(mHdr.getRoSegmentInfo().file_layout.offset, tc::io::SeekOrigin::Begin);
mFile->read(mRoBlob.data(), mRoBlob.size());
}
#else
mRoBlob.alloc(mHdr.getRoSegmentInfo().file_layout.size);
(*mFile)->read(mRoBlob.data(), mHdr.getRoSegmentInfo().file_layout.offset, mRoBlob.size());
#endif
// process data segment
#ifdef _KIP_COMPRESSION_IMPLEMENTED
// process ro segment
if (mHdr.getDataSegmentInfo().is_compressed)
{
scratch.alloc(mHdr.getDataSegmentInfo().file_layout.size);
(*mFile)->read(scratch.data(), mHdr.getDataSegmentInfo().file_layout.offset, scratch.size());
mDataBlob.alloc(mHdr.getDataSegmentInfo().memory_layout.size);
fnd::lz4::decompressData(scratch.data(), (uint32_t)scratch.size(), mDataBlob.data(), (uint32_t)mDataBlob.size(), decompressed_len);
if (decompressed_len != mDataBlob.size())
// allocate/read compressed ro segment
scratch = tc::ByteData(mHdr.getDataSegmentInfo().file_layout.size);
mFile->seek(mHdr.getDataSegmentInfo().file_layout.offset, tc::io::SeekOrigin::Begin);
mFile->read(scratch.data(), scratch.size());
// allocate for decompressed ro segment
mDataBlob = tc::ByteData(mHdr.getDataSegmentInfo().memory_layout.size);
// decompress ro segment
if (decompressData(scratch.data(), scratch.size(), mDataBlob.data(), mDataBlob.size()) != mDataBlob.size())
{
throw fnd::Exception(kModuleName, "KIP data segment failed to decompress");
throw tc::Exception(mModuleName, "KIP data segment failed to decompress");
}
}
else
{
mDataBlob.alloc(mHdr.getDataSegmentInfo().file_layout.size);
(*mFile)->read(mDataBlob.data(), mHdr.getDataSegmentInfo().file_layout.offset, mDataBlob.size());
// read ro segment directly (not compressed)
mDataBlob = tc::ByteData(mHdr.getDataSegmentInfo().file_layout.size);
mFile->seek(mHdr.getDataSegmentInfo().file_layout.offset, tc::io::SeekOrigin::Begin);
mFile->read(mDataBlob.data(), mDataBlob.size());
}
#else
mDataBlob.alloc(mHdr.getDataSegmentInfo().file_layout.size);
(*mFile)->read(mDataBlob.data(), mHdr.getDataSegmentInfo().file_layout.offset, mDataBlob.size());
#endif
}
void KipProcess::displayHeader()
size_t nstool::KipProcess::decompressData(const byte_t* src, size_t src_len, byte_t* dst, size_t dst_capacity)
{
std::cout << "[KIP Header]" << std::endl;
std::cout << " Meta:" << std::endl;
std::cout << " Name: " << mHdr.getName() << std::endl;
std::cout << " TitleId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mHdr.getTitleId() << std::endl;
std::cout << " Version: v" << std::dec << mHdr.getVersion() << std::endl;
std::cout << " Is64BitInstruction: " << std::boolalpha << mHdr.getIs64BitInstructionFlag() << std::endl;
std::cout << " Is64BitAddressSpace: " << std::boolalpha << mHdr.getIs64BitAddressSpaceFlag() << std::endl;
std::cout << " UseSecureMemory: " << std::boolalpha << mHdr.getUseSecureMemoryFlag() << std::endl;
std::cout << " Program Sections:" << std::endl;
std::cout << " .text:" << std::endl;
if (_HAS_BIT(mCliOutputMode, OUTPUT_LAYOUT))
throw tc::NotImplementedException(mModuleName, "KIP decompression not implemented yet.");
}
void nstool::KipProcess::displayHeader()
{
fmt::print("[KIP Header]\n");
fmt::print(" Meta:\n");
fmt::print(" Name: {:s}\n", mHdr.getName());
fmt::print(" TitleId: 0x{:016x}\n", mHdr.getTitleId());
fmt::print(" Version: v{:d}\n", mHdr.getVersion());
fmt::print(" Is64BitInstruction: {}\n", mHdr.getIs64BitInstructionFlag());
fmt::print(" Is64BitAddressSpace: {}\n", mHdr.getIs64BitAddressSpaceFlag());
fmt::print(" UseSecureMemory: {}\n", mHdr.getUseSecureMemoryFlag());
fmt::print(" Program Sections:\n");
fmt::print(" .text:\n");
if (mCliOutputMode.show_layout)
{
std::cout << " FileOffset: 0x" << std::hex << mHdr.getTextSegmentInfo().file_layout.offset << std::endl;
std::cout << " FileSize: 0x" << std::hex << mHdr.getTextSegmentInfo().file_layout.size << (mHdr.getTextSegmentInfo().is_compressed? " (COMPRESSED)" : "") << std::endl;
fmt::print(" FileOffset: 0x{:x}\n", mHdr.getTextSegmentInfo().file_layout.offset);
fmt::print(" FileSize: 0x{:x}{:s}\n", mHdr.getTextSegmentInfo().file_layout.size, (mHdr.getTextSegmentInfo().is_compressed? " (COMPRESSED)" : ""));
}
std::cout << " MemoryOffset: 0x" << std::hex << mHdr.getTextSegmentInfo().memory_layout.offset << std::endl;
std::cout << " MemorySize: 0x" << std::hex << mHdr.getTextSegmentInfo().memory_layout.size << std::endl;
std::cout << " .ro:" << std::endl;
if (_HAS_BIT(mCliOutputMode, OUTPUT_LAYOUT))
fmt::print(" MemoryOffset: 0x{:x}\n", mHdr.getTextSegmentInfo().memory_layout.offset);
fmt::print(" MemorySize: 0x{:x}\n", mHdr.getTextSegmentInfo().memory_layout.size);
fmt::print(" .ro:\n");
if (mCliOutputMode.show_layout)
{
std::cout << " FileOffset: 0x" << std::hex << mHdr.getRoSegmentInfo().file_layout.offset << std::endl;
std::cout << " FileSize: 0x" << std::hex << mHdr.getRoSegmentInfo().file_layout.size << (mHdr.getRoSegmentInfo().is_compressed? " (COMPRESSED)" : "") << std::endl;
fmt::print(" FileOffset: 0x{:x}\n", mHdr.getRoSegmentInfo().file_layout.offset);
fmt::print(" FileSize: 0x{:x}{:s}\n", mHdr.getRoSegmentInfo().file_layout.size, (mHdr.getRoSegmentInfo().is_compressed? " (COMPRESSED)" : ""));
}
std::cout << " MemoryOffset: 0x" << std::hex << mHdr.getRoSegmentInfo().memory_layout.offset << std::endl;
std::cout << " MemorySize: 0x" << std::hex << mHdr.getRoSegmentInfo().memory_layout.size << std::endl;
std::cout << " .data:" << std::endl;
if (_HAS_BIT(mCliOutputMode, OUTPUT_LAYOUT))
fmt::print(" MemoryOffset: 0x{:x}\n", mHdr.getRoSegmentInfo().memory_layout.offset);
fmt::print(" MemorySize: 0x{:x}\n", mHdr.getRoSegmentInfo().memory_layout.size);
fmt::print(" .data:\n");
if (mCliOutputMode.show_layout)
{
std::cout << " FileOffset: 0x" << std::hex << mHdr.getDataSegmentInfo().file_layout.offset << std::endl;
std::cout << " FileSize: 0x" << std::hex << mHdr.getDataSegmentInfo().file_layout.size << (mHdr.getDataSegmentInfo().is_compressed? " (COMPRESSED)" : "") << std::endl;
fmt::print(" FileOffset: 0x{:x}\n", mHdr.getDataSegmentInfo().file_layout.offset);
fmt::print(" FileSize: 0x{:x}{:s}\n", mHdr.getDataSegmentInfo().file_layout.size, (mHdr.getDataSegmentInfo().is_compressed? " (COMPRESSED)" : ""));
}
std::cout << " MemoryOffset: 0x" << std::hex << mHdr.getDataSegmentInfo().memory_layout.offset << std::endl;
std::cout << " MemorySize: 0x" << std::hex << mHdr.getDataSegmentInfo().memory_layout.size << std::endl;
std::cout << " .bss:" << std::endl;
std::cout << " MemorySize: 0x" << std::hex << mHdr.getBssSize() << std::endl;
fmt::print(" MemoryOffset: 0x{:x}\n", mHdr.getDataSegmentInfo().memory_layout.offset);
fmt::print(" MemorySize: 0x{:x}\n", mHdr.getDataSegmentInfo().memory_layout.size);
fmt::print(" .bss:\n");
fmt::print(" MemorySize: 0x{:x}\n", mHdr.getBssSize());
}
void KipProcess::displayKernelCap(const nn::hac::KernelCapabilityControl& kern)
void nstool::KipProcess::displayKernelCap(const pie::hac::KernelCapabilityControl& kern)
{
std::cout << "[Kernel Capabilities]" << std::endl;
fmt::print("[Kernel Capabilities]\n");
if (kern.getThreadInfo().isSet())
{
nn::hac::ThreadInfoHandler threadInfo = kern.getThreadInfo();
std::cout << " Thread Priority:" << std::endl;
std::cout << " Min: " << std::dec << (uint32_t)threadInfo.getMinPriority() << std::endl;
std::cout << " Max: " << std::dec << (uint32_t)threadInfo.getMaxPriority() << std::endl;
std::cout << " CpuId:" << std::endl;
std::cout << " Min: " << std::dec << (uint32_t)threadInfo.getMinCpuId() << std::endl;
std::cout << " Max: " << std::dec << (uint32_t)threadInfo.getMaxCpuId() << std::endl;
pie::hac::ThreadInfoHandler threadInfo = kern.getThreadInfo();
fmt::print(" Thread Priority:\n");
fmt::print(" Min: {:d}\n", threadInfo.getMinPriority());
fmt::print(" Max: {:d}\n", threadInfo.getMaxPriority());
fmt::print(" CpuId:\n");
fmt::print(" Min: {:d}\n", threadInfo.getMinCpuId());
fmt::print(" Max: {:d}\n", threadInfo.getMaxCpuId());
}
if (kern.getSystemCalls().isSet())
{
auto syscall_ids = kern.getSystemCalls().getSystemCallIds();
std::cout << " SystemCalls:" << std::endl;
fmt::print(" SystemCalls:\n");
std::vector<std::string> syscall_names;
for (size_t syscall_id = 0; syscall_id < syscall_ids.size(); syscall_id++)
{
if (syscall_ids.test(syscall_id))
syscall_names.push_back(nn::hac::KernelCapabilityUtil::getSystemCallIdAsString(nn::hac::kc::SystemCallId(syscall_id)));
syscall_names.push_back(pie::hac::KernelCapabilityUtil::getSystemCallIdAsString(pie::hac::kc::SystemCallId(syscall_id)));
}
fnd::SimpleTextOutput::dumpStringList(syscall_names, 60, 4);
fmt::print("{:s}", tc::cli::FormatUtil::formatListWithLineLimit(syscall_names, 60, 4));
}
if (kern.getMemoryMaps().isSet())
{
fnd::List<nn::hac::MemoryMappingHandler::sMemoryMapping> maps = kern.getMemoryMaps().getMemoryMaps();
fnd::List<nn::hac::MemoryMappingHandler::sMemoryMapping> ioMaps = kern.getMemoryMaps().getIoMemoryMaps();
auto maps = kern.getMemoryMaps().getMemoryMaps();
auto ioMaps = kern.getMemoryMaps().getIoMemoryMaps();
std::cout << " MemoryMaps:" << std::endl;
fmt::print(" MemoryMaps:\n");
for (size_t i = 0; i < maps.size(); i++)
{
std::cout << " 0x" << std::hex << std::setw(16) << std::setfill('0') << ((uint64_t)maps[i].addr << 12) << " - 0x" << std::hex << std::setw(16) << std::setfill('0') << (((uint64_t)(maps[i].addr + maps[i].size) << 12) - 1) << " (perm=" << nn::hac::KernelCapabilityUtil::getMemoryPermissionAsString(maps[i].perm) << ") (type=" << nn::hac::KernelCapabilityUtil::getMappingTypeAsString(maps[i].type) << ")" << std::endl;
fmt::print(" {:s}\n", formatMappingAsString(maps[i]));
}
//std::cout << " IoMaps:" << std::endl;
//fmt::print(" IoMaps:\n");
for (size_t i = 0; i < ioMaps.size(); i++)
{
std::cout << " 0x" << std::hex << std::setw(16) << std::setfill('0') << ((uint64_t)ioMaps[i].addr << 12) << " - 0x" << std::hex << std::setw(16) << std::setfill('0') << (((uint64_t)(ioMaps[i].addr + ioMaps[i].size) << 12) - 1) << " (perm=" << nn::hac::KernelCapabilityUtil::getMemoryPermissionAsString(ioMaps[i].perm) << ") (type=" << nn::hac::KernelCapabilityUtil::getMappingTypeAsString(ioMaps[i].type) << ")" << std::endl;
fmt::print(" {:s}\n", formatMappingAsString(ioMaps[i]));
}
}
if (kern.getInterupts().isSet())
{
fnd::List<uint16_t> interupts = kern.getInterupts().getInteruptList();
std::cout << " Interupts Flags:" << std::endl;
for (uint32_t i = 0; i < interupts.size(); i++)
std::vector<std::string> interupts;
for (auto itr = kern.getInterupts().getInteruptList().begin(); itr != kern.getInterupts().getInteruptList().end(); itr++)
{
if (i % 10 == 0)
{
if (i != 0)
std::cout << std::endl;
std::cout << " ";
}
std::cout << "0x" << std::hex << (uint32_t)interupts[i];
if (interupts[i] != interupts.atBack())
std::cout << ", ";
interupts.push_back(fmt::format("0x{:x}", *itr));
}
std::cout << std::endl;
fmt::print(" Interupts Flags:\n");
fmt::print("{:s}", tc::cli::FormatUtil::formatListWithLineLimit(interupts, 60, 4));
}
if (kern.getMiscParams().isSet())
{
std::cout << " ProgramType: " << nn::hac::KernelCapabilityUtil::getProgramTypeAsString(kern.getMiscParams().getProgramType()) << " (" << std::dec << (uint32_t)kern.getMiscParams().getProgramType() << ")" << std::endl;
fmt::print(" ProgramType: {:s} ({:d})\n", pie::hac::KernelCapabilityUtil::getProgramTypeAsString(kern.getMiscParams().getProgramType()), (uint32_t)kern.getMiscParams().getProgramType());
}
if (kern.getKernelVersion().isSet())
{
std::cout << " Kernel Version: " << std::dec << (uint32_t)kern.getKernelVersion().getVerMajor() << "." << (uint32_t)kern.getKernelVersion().getVerMinor() << std::endl;
fmt::print(" Kernel Version: {:d}.{:d}\n", kern.getKernelVersion().getVerMajor(), kern.getKernelVersion().getVerMinor());
}
if (kern.getHandleTableSize().isSet())
{
std::cout << " Handle Table Size: 0x" << std::hex << kern.getHandleTableSize().getHandleTableSize() << std::endl;
fmt::print(" Handle Table Size: 0x{:x}\n", kern.getHandleTableSize().getHandleTableSize());
}
if (kern.getMiscFlags().isSet())
{
auto misc_flags = kern.getMiscFlags().getMiscFlags();
std::cout << " Misc Flags:" << std::endl;
fmt::print(" Misc Flags:\n");
std::vector<std::string> misc_flags_names;
for (size_t misc_flags_bit = 0; misc_flags_bit < misc_flags.size(); misc_flags_bit++)
{
if (misc_flags.test(misc_flags_bit))
misc_flags_names.push_back(nn::hac::KernelCapabilityUtil::getMiscFlagsBitAsString(nn::hac::kc::MiscFlagsBit(misc_flags_bit)));
misc_flags_names.push_back(pie::hac::KernelCapabilityUtil::getMiscFlagsBitAsString(pie::hac::kc::MiscFlagsBit(misc_flags_bit)));
}
fnd::SimpleTextOutput::dumpStringList(misc_flags_names, 60, 4);
fmt::print("{:s}", tc::cli::FormatUtil::formatListWithLineLimit(misc_flags_names, 60, 4));
}
}
std::string nstool::KipProcess::formatMappingAsString(const pie::hac::MemoryMappingHandler::sMemoryMapping& map) const
{
return fmt::format("0x{:016x} - 0x{:016x} (perm={:s}) (type={:s})", ((uint64_t)map.addr << 12), (((uint64_t)(map.addr + map.size) << 12) - 1), pie::hac::KernelCapabilityUtil::getMemoryPermissionAsString(map.perm), pie::hac::KernelCapabilityUtil::getMappingTypeAsString(map.type));
}

View file

@ -1,12 +1,9 @@
#pragma once
#include <vector>
#include <string>
#include <fnd/types.h>
#include <fnd/IFile.h>
#include <fnd/SharedPtr.h>
#include <nn/hac/KernelInitialProcessHeader.h>
#include "types.h"
#include "common.h"
#include <pietendo/hac/KernelInitialProcessHeader.h>
namespace nstool {
class KipProcess
{
@ -15,21 +12,26 @@ public:
void process();
void setInputFile(const fnd::SharedPtr<fnd::IFile>& file);
void setInputFile(const std::shared_ptr<tc::io::IStream>& file);
void setCliOutputMode(CliOutputMode type);
void setVerifyMode(bool verify);
private:
const std::string kModuleName = "KipProcess";
std::string mModuleName;
fnd::SharedPtr<fnd::IFile> mFile;
std::shared_ptr<tc::io::IStream> mFile;
CliOutputMode mCliOutputMode;
bool mVerify;
nn::hac::KernelInitialProcessHeader mHdr;
fnd::Vec<byte_t> mTextBlob, mRoBlob, mDataBlob;
pie::hac::KernelInitialProcessHeader mHdr;
tc::ByteData mTextBlob, mRoBlob, mDataBlob;
void importHeader();
void importCodeSegments();
size_t decompressData(const byte_t* src, size_t src_len, byte_t* dst, size_t dst_capacity);
void displayHeader();
void displayKernelCap(const nn::hac::KernelCapabilityControl& kern);
};
void displayKernelCap(const pie::hac::KernelCapabilityControl& kern);
std::string formatMappingAsString(const pie::hac::MemoryMappingHandler::sMemoryMapping& map) const;
};
}

View file

@ -1,23 +1,19 @@
#include "MetaProcess.h"
#include <iostream>
#include <iomanip>
#include <pietendo/hac/AccessControlInfoUtil.h>
#include <pietendo/hac/FileSystemAccessUtil.h>
#include <pietendo/hac/KernelCapabilityUtil.h>
#include <pietendo/hac/MetaUtil.h>
#include <nn/hac/AccessControlInfoUtil.h>
#include <nn/hac/FileSystemAccessUtil.h>
#include <nn/hac/KernelCapabilityUtil.h>
#include <nn/hac/MetaUtil.h>
#include <fnd/SimpleTextOutput.h>
MetaProcess::MetaProcess() :
nstool::MetaProcess::MetaProcess() :
mModuleName("nstool::MetaProcess"),
mFile(),
mCliOutputMode(_BIT(OUTPUT_BASIC)),
mCliOutputMode(true, false, false, false),
mVerify(false)
{
}
void MetaProcess::process()
void nstool::MetaProcess::process()
{
importMeta();
@ -27,7 +23,7 @@ void MetaProcess::process()
validateAciFromAcid(mMeta.getAccessControlInfo(), mMeta.getAccessControlInfoDesc());
}
if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
if (mCliOutputMode.show_basic_info)
{
// npdm binary
displayMetaHeader(mMeta);
@ -39,7 +35,7 @@ void MetaProcess::process()
displayKernelCap(mMeta.getAccessControlInfo().getKernelCapabilities());
// acid binary
if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mCliOutputMode.show_extended_info)
{
displayAciDescHdr(mMeta.getAccessControlInfoDesc());
displayFac(mMeta.getAccessControlInfoDesc().getFileSystemAccessControl());
@ -49,80 +45,99 @@ void MetaProcess::process()
}
}
void MetaProcess::setInputFile(const fnd::SharedPtr<fnd::IFile>& file)
void nstool::MetaProcess::setInputFile(const std::shared_ptr<tc::io::IStream>& file)
{
mFile = file;
}
void MetaProcess::setKeyCfg(const KeyConfiguration& keycfg)
void nstool::MetaProcess::setKeyCfg(const KeyBag& keycfg)
{
mKeyCfg = keycfg;
}
void MetaProcess::setCliOutputMode(CliOutputMode type)
void nstool::MetaProcess::setCliOutputMode(CliOutputMode type)
{
mCliOutputMode = type;
}
void MetaProcess::setVerifyMode(bool verify)
void nstool::MetaProcess::setVerifyMode(bool verify)
{
mVerify = verify;
}
const nn::hac::Meta& MetaProcess::getMeta() const
const pie::hac::Meta& nstool::MetaProcess::getMeta() const
{
return mMeta;
}
void MetaProcess::importMeta()
void nstool::MetaProcess::importMeta()
{
fnd::Vec<byte_t> scratch;
if (*mFile == nullptr)
if (mFile == nullptr)
{
throw fnd::Exception(kModuleName, "No file reader set.");
throw tc::Exception(mModuleName, "No file reader set.");
}
if (mFile->canRead() == false || mFile->canSeek() == false)
{
throw tc::NotSupportedException(mModuleName, "Input stream requires read/seek permissions.");
}
scratch.alloc((*mFile)->size());
(*mFile)->read(scratch.data(), 0, scratch.size());
// check if file_size is greater than 20MB, don't import.
size_t file_size = tc::io::IOUtil::castInt64ToSize(mFile->length());
if (file_size > (0x100000 * 20))
{
throw tc::Exception(mModuleName, "File too large.");
}
// read meta
tc::ByteData scratch = tc::ByteData(file_size);
mFile->seek(0, tc::io::SeekOrigin::Begin);
mFile->read(scratch.data(), scratch.size());
mMeta.fromBytes(scratch.data(), scratch.size());
}
void MetaProcess::validateAcidSignature(const nn::hac::AccessControlInfoDesc& acid, byte_t key_generation)
void nstool::MetaProcess::validateAcidSignature(const pie::hac::AccessControlInfoDesc& acid, byte_t key_generation)
{
try {
fnd::rsa::sRsa2048Key acid_sign_key;
if (mKeyCfg.getAcidSignKey(acid_sign_key, key_generation) != true)
throw fnd::Exception();
if (mKeyCfg.acid_sign_key.find(key_generation) == mKeyCfg.acid_sign_key.end())
{
throw tc::Exception("Failed to load rsa public key");
}
acid.validateSignature(acid_sign_key);
acid.validateSignature(mKeyCfg.acid_sign_key.at(key_generation));
}
catch (...) {
std::cout << "[WARNING] ACID Signature: FAIL" << std::endl;
catch (tc::Exception& e) {
fmt::print("[WARNING] ACID Signature: FAIL ({:s})\n", e.error());
}
}
void MetaProcess::validateAciFromAcid(const nn::hac::AccessControlInfo& aci, const nn::hac::AccessControlInfoDesc& acid)
void nstool::MetaProcess::validateAciFromAcid(const pie::hac::AccessControlInfo& aci, const pie::hac::AccessControlInfoDesc& acid)
{
// check Program ID
if (acid.getProgramIdRestrict().min > 0 && aci.getProgramId() < acid.getProgramIdRestrict().min)
{
std::cout << "[WARNING] ACI ProgramId: FAIL (Outside Legal Range)" << std::endl;
fmt::print("[WARNING] ACI ProgramId: FAIL (Outside Legal Range)\n");
}
else if (acid.getProgramIdRestrict().max > 0 && aci.getProgramId() > acid.getProgramIdRestrict().max)
{
std::cout << "[WARNING] ACI ProgramId: FAIL (Outside Legal Range)" << std::endl;
fmt::print("[WARNING] ACI ProgramId: FAIL (Outside Legal Range)\n");
}
auto fs_access = aci.getFileSystemAccessControl().getFsAccess();
auto desc_fs_access = acid.getFileSystemAccessControl().getFsAccess();
for (size_t i = 0; i < fs_access.size(); i++)
{
if (fs_access.test(i) && desc_fs_access.test(i) == false)
bool rightFound = false;
for (size_t j = 0; j < desc_fs_access.size() && rightFound == false; j++)
{
std::cout << "[WARNING] ACI/FAC FsaRights: FAIL (" << nn::hac::FileSystemAccessUtil::getFsAccessFlagAsString(nn::hac::fac::FsAccessFlag(i)) << " not permitted)" << std::endl;
if (fs_access[i] == desc_fs_access[j])
rightFound = true;
}
if (rightFound == false)
{
fmt::print("[WARNING] ACI/FAC FsaRights: FAIL ({:s} not permitted)\n", pie::hac::FileSystemAccessUtil::getFsAccessFlagAsString(fs_access[i]));
}
}
@ -138,10 +153,13 @@ void MetaProcess::validateAciFromAcid(const nn::hac::AccessControlInfo& aci, con
if (rightFound == false)
{
std::cout << "[WARNING] ACI/FAC ContentOwnerId: FAIL (" << std::hex << std::setw(16) << std::setfill('0') << aci.getFileSystemAccessControl().getContentOwnerIdList()[i] << " not permitted)" << std::endl;
fmt::print("[WARNING] ACI/FAC ContentOwnerId: FAIL (0x{:016x} not permitted)\n", aci.getFileSystemAccessControl().getContentOwnerIdList()[i]);
}
}
// See https://github.com/jakcron/nstool/issues/92
// Nintendo doesn't populate SaveDataOwnerIdList in ACID, so this field cannot be verified
#if 0
for (size_t i = 0; i < aci.getFileSystemAccessControl().getSaveDataOwnerIdList().size(); i++)
{
bool rightFound = false;
@ -154,9 +172,10 @@ void MetaProcess::validateAciFromAcid(const nn::hac::AccessControlInfo& aci, con
if (rightFound == false)
{
std::cout << "[WARNING] ACI/FAC SaveDataOwnerId: FAIL (" << std::hex << std::setw(16) << std::setfill('0') << aci.getFileSystemAccessControl().getSaveDataOwnerIdList()[i].id << "(" << std::dec << (uint32_t)aci.getFileSystemAccessControl().getSaveDataOwnerIdList()[i].access_type << ") not permitted)" << std::endl;
fmt::print("[WARNING] ACI/FAC SaveDataOwnerId: FAIL (0x{:016x} ({:d}) not permitted)\n", aci.getFileSystemAccessControl().getSaveDataOwnerIdList()[i].id, (uint32_t)aci.getFileSystemAccessControl().getSaveDataOwnerIdList()[i].access_type);
}
}
#endif
// check SAC
for (size_t i = 0; i < aci.getServiceAccessControl().getServiceList().size(); i++)
@ -170,7 +189,7 @@ void MetaProcess::validateAciFromAcid(const nn::hac::AccessControlInfo& aci, con
if (rightFound == false)
{
std::cout << "[WARNING] ACI/SAC ServiceList: FAIL (" << aci.getServiceAccessControl().getServiceList()[i].getName() << (aci.getServiceAccessControl().getServiceList()[i].isServer()? " (Server)" : "") << " not permitted)" << std::endl;
fmt::print("[WARNING] ACI/SAC ServiceList: FAIL ({:s}{:s} not permitted)\n", aci.getServiceAccessControl().getServiceList()[i].getName(), (aci.getServiceAccessControl().getServiceList()[i].isServer()? " (Server)" : ""));
}
}
@ -178,19 +197,19 @@ void MetaProcess::validateAciFromAcid(const nn::hac::AccessControlInfo& aci, con
// check thread info
if (aci.getKernelCapabilities().getThreadInfo().getMaxCpuId() != acid.getKernelCapabilities().getThreadInfo().getMaxCpuId())
{
std::cout << "[WARNING] ACI/KC ThreadInfo/MaxCpuId: FAIL (" << std::dec << (uint32_t)aci.getKernelCapabilities().getThreadInfo().getMaxCpuId() << " not permitted)" << std::endl;
fmt::print("[WARNING] ACI/KC ThreadInfo/MaxCpuId: FAIL ({:d} not permitted)\n", aci.getKernelCapabilities().getThreadInfo().getMaxCpuId());
}
if (aci.getKernelCapabilities().getThreadInfo().getMinCpuId() != acid.getKernelCapabilities().getThreadInfo().getMinCpuId())
{
std::cout << "[WARNING] ACI/KC ThreadInfo/MinCpuId: FAIL (" << std::dec << (uint32_t)aci.getKernelCapabilities().getThreadInfo().getMinCpuId() << " not permitted)" << std::endl;
fmt::print("[WARNING] ACI/KC ThreadInfo/MinCpuId: FAIL ({:d} not permitted)\n", aci.getKernelCapabilities().getThreadInfo().getMinCpuId());
}
if (aci.getKernelCapabilities().getThreadInfo().getMaxPriority() != acid.getKernelCapabilities().getThreadInfo().getMaxPriority())
{
std::cout << "[WARNING] ACI/KC ThreadInfo/MaxPriority: FAIL (" << std::dec << (uint32_t)aci.getKernelCapabilities().getThreadInfo().getMaxPriority() << " not permitted)" << std::endl;
fmt::print("[WARNING] ACI/KC ThreadInfo/MaxPriority: FAIL ({:d} not permitted)\n", aci.getKernelCapabilities().getThreadInfo().getMaxPriority());
}
if (aci.getKernelCapabilities().getThreadInfo().getMinPriority() != acid.getKernelCapabilities().getThreadInfo().getMinPriority())
{
std::cout << "[WARNING] ACI/KC ThreadInfo/MinPriority: FAIL (" << std::dec << (uint32_t)aci.getKernelCapabilities().getThreadInfo().getMinPriority() << " not permitted)" << std::endl;
fmt::print("[WARNING] ACI/KC ThreadInfo/MinPriority: FAIL ({:d} not permitted)\n", aci.getKernelCapabilities().getThreadInfo().getMinPriority());
}
// check system calls
auto syscall_ids = aci.getKernelCapabilities().getSystemCalls().getSystemCallIds();
@ -199,7 +218,7 @@ void MetaProcess::validateAciFromAcid(const nn::hac::AccessControlInfo& aci, con
{
if (syscall_ids.test(i) && desc_syscall_ids.test(i) == false)
{
std::cout << "[WARNING] ACI/KC SystemCallList: FAIL (" << nn::hac::KernelCapabilityUtil::getSystemCallIdAsString(nn::hac::kc::SystemCallId(i)) << " not permitted)" << std::endl;
fmt::print("[WARNING] ACI/KC SystemCallList: FAIL ({:s} not permitted)\n", pie::hac::KernelCapabilityUtil::getSystemCallIdAsString(pie::hac::kc::SystemCallId(i)));
}
}
// check memory maps
@ -216,7 +235,7 @@ void MetaProcess::validateAciFromAcid(const nn::hac::AccessControlInfo& aci, con
{
auto map = aci.getKernelCapabilities().getMemoryMaps().getMemoryMaps()[i];
std::cout << "[WARNING] ACI/KC MemoryMap: FAIL (0x" << std::hex << std::setw(16) << std::setfill('0') << ((uint64_t)map.addr << 12) << " - 0x" << std::hex << std::setw(16) << std::setfill('0') << (((uint64_t)(map.addr + map.size) << 12) - 1) << " (perm=" << nn::hac::KernelCapabilityUtil::getMemoryPermissionAsString(map.perm) << ") (type=" << nn::hac::KernelCapabilityUtil::getMappingTypeAsString(map.type) << ") not permitted)" << std::endl;
fmt::print("[WARNING] ACI/KC MemoryMap: FAIL ({:s} not permitted)\n", formatMappingAsString(map));
}
}
for (size_t i = 0; i < aci.getKernelCapabilities().getMemoryMaps().getIoMemoryMaps().size(); i++)
@ -232,7 +251,7 @@ void MetaProcess::validateAciFromAcid(const nn::hac::AccessControlInfo& aci, con
{
auto map = aci.getKernelCapabilities().getMemoryMaps().getIoMemoryMaps()[i];
std::cout << "[WARNING] ACI/KC IoMemoryMap: FAIL (0x" << std::hex << std::setw(16) << std::setfill('0') << ((uint64_t)map.addr << 12) << " - 0x" << std::hex << std::setw(16) << std::setfill('0') << (((uint64_t)(map.addr + map.size) << 12) - 1) << " (perm=" << nn::hac::KernelCapabilityUtil::getMemoryPermissionAsString(map.perm) << ") (type=" << nn::hac::KernelCapabilityUtil::getMappingTypeAsString(map.type) << ") not permitted)" << std::endl;
fmt::print("[WARNING] ACI/KC IoMemoryMap: FAIL ({:s} not permitted)\n", formatMappingAsString(map));
}
}
// check interupts
@ -247,25 +266,25 @@ void MetaProcess::validateAciFromAcid(const nn::hac::AccessControlInfo& aci, con
if (rightFound == false)
{
std::cout << "[WARNING] ACI/KC InteruptsList: FAIL (0x" << std::hex << (uint32_t)aci.getKernelCapabilities().getInterupts().getInteruptList()[i] << " not permitted)" << std::endl;
fmt::print("[WARNING] ACI/KC InteruptsList: FAIL (0x{:x} not permitted)\n", aci.getKernelCapabilities().getInterupts().getInteruptList()[i]);
}
}
// check misc params
if (aci.getKernelCapabilities().getMiscParams().getProgramType() != acid.getKernelCapabilities().getMiscParams().getProgramType())
{
std::cout << "[WARNING] ACI/KC ProgramType: FAIL (" << std::dec << (uint32_t)aci.getKernelCapabilities().getMiscParams().getProgramType() << " not permitted)" << std::endl;
fmt::print("[WARNING] ACI/KC ProgramType: FAIL ({:d} not permitted)\n", (uint32_t)aci.getKernelCapabilities().getMiscParams().getProgramType());
}
// check kernel version
uint32_t aciKernelVersion = (uint32_t)aci.getKernelCapabilities().getKernelVersion().getVerMajor() << 16 | (uint32_t)aci.getKernelCapabilities().getKernelVersion().getVerMinor();
uint32_t acidKernelVersion = (uint32_t)acid.getKernelCapabilities().getKernelVersion().getVerMajor() << 16 | (uint32_t)acid.getKernelCapabilities().getKernelVersion().getVerMinor();
if (aciKernelVersion < acidKernelVersion)
{
std::cout << "[WARNING] ACI/KC RequiredKernelVersion: FAIL (" << std::dec << aci.getKernelCapabilities().getKernelVersion().getVerMajor() << "." << aci.getKernelCapabilities().getKernelVersion().getVerMinor() << " not permitted)" << std::endl;
fmt::print("[WARNING] ACI/KC RequiredKernelVersion: FAIL ({:d}.{:d} not permitted)\n", aci.getKernelCapabilities().getKernelVersion().getVerMajor(), aci.getKernelCapabilities().getKernelVersion().getVerMinor());
}
// check handle table size
if (aci.getKernelCapabilities().getHandleTableSize().getHandleTableSize() > acid.getKernelCapabilities().getHandleTableSize().getHandleTableSize())
{
std::cout << "[WARNING] ACI/KC HandleTableSize: FAIL (0x" << std::hex << (uint32_t)aci.getKernelCapabilities().getHandleTableSize().getHandleTableSize() << " too large)" << std::endl;
fmt::print("[WARNING] ACI/KC HandleTableSize: FAIL (0x{:x} too large)\n", aci.getKernelCapabilities().getHandleTableSize().getHandleTableSize());
}
// check misc flags
auto misc_flags = aci.getKernelCapabilities().getMiscFlags().getMiscFlags();
@ -274,205 +293,187 @@ void MetaProcess::validateAciFromAcid(const nn::hac::AccessControlInfo& aci, con
{
if (misc_flags.test(i) && desc_misc_flags.test(i) == false)
{
std::cout << "[WARNING] ACI/KC MiscFlag: FAIL (" << nn::hac::KernelCapabilityUtil::getMiscFlagsBitAsString(nn::hac::kc::MiscFlagsBit(i)) << " not permitted)" << std::endl;
fmt::print("[WARNING] ACI/KC MiscFlag: FAIL ({:s} not permitted)\n", pie::hac::KernelCapabilityUtil::getMiscFlagsBitAsString(pie::hac::kc::MiscFlagsBit(i)));
}
}
}
void MetaProcess::displayMetaHeader(const nn::hac::Meta& hdr)
void nstool::MetaProcess::displayMetaHeader(const pie::hac::Meta& hdr)
{
std::cout << "[Meta Header]" << std::endl;
std::cout << " ACID KeyGeneration: " << std::dec << (uint32_t)hdr.getAccessControlInfoDescKeyGeneration() << std::endl;
std::cout << " Flags:" << std::endl;
std::cout << " Is64BitInstruction: " << std::boolalpha << hdr.getIs64BitInstructionFlag() << std::endl;
std::cout << " ProcessAddressSpace: " << nn::hac::MetaUtil::getProcessAddressSpaceAsString(hdr.getProcessAddressSpace()) << std::endl;
std::cout << " OptimizeMemoryAllocation: " << std::boolalpha << hdr.getOptimizeMemoryAllocationFlag() << std::endl;
std::cout << " SystemResourceSize: 0x" << std::hex << hdr.getSystemResourceSize() << std::endl;
std::cout << " Main Thread Params:" << std::endl;
std::cout << " Priority: " << std::dec << (uint32_t)hdr.getMainThreadPriority() << std::endl;
std::cout << " CpuId: " << std::dec << (uint32_t)hdr.getMainThreadCpuId() << std::endl;
std::cout << " StackSize: 0x" << std::hex << hdr.getMainThreadStackSize() << std::endl;
std::cout << " TitleInfo:" << std::endl;
std::cout << " Version: v" << std::dec << hdr.getVersion() << std::endl;
std::cout << " Name: " << hdr.getName() << std::endl;
fmt::print("[Meta Header]\n");
fmt::print(" ACID KeyGeneration: {:d}\n", hdr.getAccessControlInfoDescKeyGeneration());
fmt::print(" Flags:\n");
fmt::print(" Is64BitInstruction: {}\n", hdr.getIs64BitInstructionFlag());
fmt::print(" ProcessAddressSpace: {:s}\n", pie::hac::MetaUtil::getProcessAddressSpaceAsString(hdr.getProcessAddressSpace()));
fmt::print(" OptimizeMemoryAllocation: {}\n", hdr.getOptimizeMemoryAllocationFlag());
fmt::print(" SystemResourceSize: 0x{:x}\n", hdr.getSystemResourceSize());
fmt::print(" Main Thread Params:\n");
fmt::print(" Priority: {:d}\n", hdr.getMainThreadPriority());
fmt::print(" CpuId: {:d}\n", hdr.getMainThreadCpuId());
fmt::print(" StackSize: 0x{:x}\n", hdr.getMainThreadStackSize());
fmt::print(" TitleInfo:\n");
fmt::print(" Version: v{:d}\n", hdr.getVersion());
fmt::print(" Name: {:s}\n", hdr.getName());
if (hdr.getProductCode().length())
{
std::cout << " ProductCode: " << hdr.getProductCode() << std::endl;
fmt::print(" ProductCode: {:s}\n", hdr.getProductCode());
}
}
void MetaProcess::displayAciHdr(const nn::hac::AccessControlInfo& aci)
void nstool::MetaProcess::displayAciHdr(const pie::hac::AccessControlInfo& aci)
{
std::cout << "[Access Control Info]" << std::endl;
std::cout << " ProgramID: 0x" << std::hex << std::setw(16) << std::setfill('0') << aci.getProgramId() << std::endl;
fmt::print("[Access Control Info]\n");
fmt::print(" ProgramID: 0x{:016x}\n", aci.getProgramId());
}
void MetaProcess::displayAciDescHdr(const nn::hac::AccessControlInfoDesc& acid)
void nstool::MetaProcess::displayAciDescHdr(const pie::hac::AccessControlInfoDesc& acid)
{
std::cout << "[Access Control Info Desc]" << std::endl;
std::cout << " Flags: " << std::endl;
std::cout << " Production: " << std::boolalpha << acid.getProductionFlag() << std::endl;
std::cout << " Unqualified Approval: " << std::boolalpha << acid.getUnqualifiedApprovalFlag() << std::endl;
std::cout << " Memory Region: " << nn::hac::AccessControlInfoUtil::getMemoryRegionAsString(acid.getMemoryRegion()) << " (" << std::dec << (uint32_t)acid.getMemoryRegion() << ")" << std::endl;
std::cout << " ProgramID Restriction" << std::endl;
std::cout << " Min: 0x" << std::hex << std::setw(16) << std::setfill('0') << acid.getProgramIdRestrict().min << std::endl;
std::cout << " Max: 0x" << std::hex << std::setw(16) << std::setfill('0') << acid.getProgramIdRestrict().max << std::endl;
fmt::print("[Access Control Info Desc]\n");
fmt::print(" Flags: \n");
fmt::print(" Production: {}\n", acid.getProductionFlag());
fmt::print(" Unqualified Approval: {}\n", acid.getUnqualifiedApprovalFlag());
fmt::print(" Memory Region: {:s} ({:d})\n", pie::hac::AccessControlInfoUtil::getMemoryRegionAsString(acid.getMemoryRegion()), (uint32_t)acid.getMemoryRegion());
fmt::print(" ProgramID Restriction\n");
fmt::print(" Min: 0x{:016x}\n", acid.getProgramIdRestrict().min);
fmt::print(" Max: 0x{:016x}\n", acid.getProgramIdRestrict().max);
}
void MetaProcess::displayFac(const nn::hac::FileSystemAccessControl& fac)
void nstool::MetaProcess::displayFac(const pie::hac::FileSystemAccessControl& fac)
{
std::cout << "[FS Access Control]" << std::endl;
std::cout << " Format Version: " << std::dec << (uint32_t)fac.getFormatVersion() << std::endl;
fmt::print("[FS Access Control]\n");
fmt::print(" Format Version: {:d}\n", fac.getFormatVersion());
auto fs_access = fac.getFsAccess();
if (fs_access.any())
if (fac.getFsAccess().size())
{
std::cout << " FsAccess:" << std::endl;
// TODO this formatting loop could be a utils function
for (size_t flag = 0, printed = 0; flag < fs_access.size(); flag++)
std::vector<std::string> fs_access_str_list;
for (auto itr = fac.getFsAccess().begin(); itr != fac.getFsAccess().end(); itr++)
{
// skip unset flags
if (fs_access.test(flag) == false)
continue;
// format the strings
// for each 10 printed we do a new line
if (printed % 10 == 0)
std::string flag_string = pie::hac::FileSystemAccessUtil::getFsAccessFlagAsString(pie::hac::fac::FsAccessFlag(*itr));
if (mCliOutputMode.show_extended_info)
{
// skip new line when we haven't printed anything
if (printed != 0)
std::cout << std::endl;
std::cout << " ";
fs_access_str_list.push_back(fmt::format("{:s} (bit {:d})", flag_string, (uint32_t)*itr));
}
// within a line we want to separate the next string from the last one with a comma and a space
else
{
std::cout << ", ";
fs_access_str_list.push_back(flag_string);
}
printed++;
// output string info
std::cout << nn::hac::FileSystemAccessUtil::getFsAccessFlagAsString(nn::hac::fac::FsAccessFlag(flag));
if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
std::cout << " (bit " << std::dec << (uint32_t)flag << ")";
}
std::cout << std::endl;
fmt::print(" FsAccess:\n");
fmt::print("{:s}", tc::cli::FormatUtil::formatListWithLineLimit(fs_access_str_list, 60, 4));
}
if (fac.getContentOwnerIdList().size())
{
std::cout << " Content Owner IDs:" << std::endl;
fmt::print(" Content Owner IDs:\n");
for (size_t i = 0; i < fac.getContentOwnerIdList().size(); i++)
{
std::cout << " 0x" << std::hex << std::setw(16) << std::setfill('0') << fac.getContentOwnerIdList()[i] << std::endl;
fmt::print(" 0x{:016x}\n", fac.getContentOwnerIdList()[i]);
}
}
if (fac.getSaveDataOwnerIdList().size())
{
std::cout << " Save Data Owner IDs:" << std::endl;
fmt::print(" Save Data Owner IDs:\n");
for (size_t i = 0; i < fac.getSaveDataOwnerIdList().size(); i++)
{
std::cout << " 0x" << std::hex << std::setw(16) << std::setfill('0') << fac.getSaveDataOwnerIdList()[i].id << " (" << nn::hac::FileSystemAccessUtil::getSaveDataOwnerAccessModeAsString(fac.getSaveDataOwnerIdList()[i].access_type) << ")" << std::endl;
fmt::print(" 0x{:016x} ({:s})\n", fac.getSaveDataOwnerIdList()[i].id, pie::hac::FileSystemAccessUtil::getSaveDataOwnerAccessModeAsString(fac.getSaveDataOwnerIdList()[i].access_type));
}
}
}
void MetaProcess::displaySac(const nn::hac::ServiceAccessControl& sac)
void nstool::MetaProcess::displaySac(const pie::hac::ServiceAccessControl& sac)
{
std::cout << "[Service Access Control]" << std::endl;
std::cout << " Service List:" << std::endl;
fmt::print("[Service Access Control]\n");
fmt::print(" Service List:\n");
std::vector<std::string> service_name_list;
for (size_t i = 0; i < sac.getServiceList().size(); i++)
{
service_name_list.push_back(sac.getServiceList()[i].getName() + (sac.getServiceList()[i].isServer() ? "(isSrv)" : ""));
}
fnd::SimpleTextOutput::dumpStringList(service_name_list, 60, 4);
fmt::print("{:s}", tc::cli::FormatUtil::formatListWithLineLimit(service_name_list, 60, 4));
}
void MetaProcess::displayKernelCap(const nn::hac::KernelCapabilityControl& kern)
void nstool::MetaProcess::displayKernelCap(const pie::hac::KernelCapabilityControl& kern)
{
std::cout << "[Kernel Capabilities]" << std::endl;
fmt::print("[Kernel Capabilities]\n");
if (kern.getThreadInfo().isSet())
{
nn::hac::ThreadInfoHandler threadInfo = kern.getThreadInfo();
std::cout << " Thread Priority:" << std::endl;
std::cout << " Min: " << std::dec << (uint32_t)threadInfo.getMinPriority() << std::endl;
std::cout << " Max: " << std::dec << (uint32_t)threadInfo.getMaxPriority() << std::endl;
std::cout << " CpuId:" << std::endl;
std::cout << " Min: " << std::dec << (uint32_t)threadInfo.getMinCpuId() << std::endl;
std::cout << " Max: " << std::dec << (uint32_t)threadInfo.getMaxCpuId() << std::endl;
pie::hac::ThreadInfoHandler threadInfo = kern.getThreadInfo();
fmt::print(" Thread Priority:\n");
fmt::print(" Min: {:d}\n", threadInfo.getMinPriority());
fmt::print(" Max: {:d}\n", threadInfo.getMaxPriority());
fmt::print(" CpuId:\n");
fmt::print(" Min: {:d}\n", threadInfo.getMinCpuId());
fmt::print(" Max: {:d}\n", threadInfo.getMaxCpuId());
}
if (kern.getSystemCalls().isSet())
{
auto syscall_ids = kern.getSystemCalls().getSystemCallIds();
std::cout << " SystemCalls:" << std::endl;
fmt::print(" SystemCalls:\n");
std::vector<std::string> syscall_names;
for (size_t syscall_id = 0; syscall_id < syscall_ids.size(); syscall_id++)
{
if (syscall_ids.test(syscall_id))
syscall_names.push_back(nn::hac::KernelCapabilityUtil::getSystemCallIdAsString(nn::hac::kc::SystemCallId(syscall_id)));
syscall_names.push_back(pie::hac::KernelCapabilityUtil::getSystemCallIdAsString(pie::hac::kc::SystemCallId(syscall_id)));
}
fnd::SimpleTextOutput::dumpStringList(syscall_names, 60, 4);
fmt::print("{:s}", tc::cli::FormatUtil::formatListWithLineLimit(syscall_names, 60, 4));
}
if (kern.getMemoryMaps().isSet())
{
auto maps = kern.getMemoryMaps().getMemoryMaps();
auto ioMaps = kern.getMemoryMaps().getIoMemoryMaps();
std::cout << " MemoryMaps:" << std::endl;
fmt::print(" MemoryMaps:\n");
for (size_t i = 0; i < maps.size(); i++)
{
std::cout << " 0x" << std::hex << std::setw(16) << std::setfill('0') << ((uint64_t)maps[i].addr << 12) << " - 0x" << std::hex << std::setw(16) << std::setfill('0') << (((uint64_t)(maps[i].addr + maps[i].size) << 12) - 1) << " (perm=" << nn::hac::KernelCapabilityUtil::getMemoryPermissionAsString(maps[i].perm) << ") (type=" << nn::hac::KernelCapabilityUtil::getMappingTypeAsString(maps[i].type) << ")" << std::endl;
fmt::print(" {:s}\n", formatMappingAsString(maps[i]));
}
//std::cout << " IoMaps:" << std::endl;
//fmt::print(" IoMaps:\n");
for (size_t i = 0; i < ioMaps.size(); i++)
{
std::cout << " 0x" << std::hex << std::setw(16) << std::setfill('0') << ((uint64_t)ioMaps[i].addr << 12) << " - 0x" << std::hex << std::setw(16) << std::setfill('0') << (((uint64_t)(ioMaps[i].addr + ioMaps[i].size) << 12) - 1) << " (perm=" << nn::hac::KernelCapabilityUtil::getMemoryPermissionAsString(ioMaps[i].perm) << ") (type=" << nn::hac::KernelCapabilityUtil::getMappingTypeAsString(ioMaps[i].type) << ")" << std::endl;
fmt::print(" {:s}\n", formatMappingAsString(ioMaps[i]));
}
}
if (kern.getInterupts().isSet())
{
fnd::List<uint16_t> interupts = kern.getInterupts().getInteruptList();
std::cout << " Interupts Flags:" << std::endl;
for (uint32_t i = 0; i < interupts.size(); i++)
std::vector<std::string> interupts;
for (auto itr = kern.getInterupts().getInteruptList().begin(); itr != kern.getInterupts().getInteruptList().end(); itr++)
{
if (i % 10 == 0)
{
if (i != 0)
std::cout << std::endl;
std::cout << " ";
}
std::cout << "0x" << std::hex << (uint32_t)interupts[i];
if (interupts[i] != interupts.atBack())
std::cout << ", ";
interupts.push_back(fmt::format("0x{:x}", *itr));
}
std::cout << std::endl;
fmt::print(" Interupts Flags:\n");
fmt::print("{:s}", tc::cli::FormatUtil::formatListWithLineLimit(interupts, 60, 4));
}
if (kern.getMiscParams().isSet())
{
std::cout << " ProgramType: " << nn::hac::KernelCapabilityUtil::getProgramTypeAsString(kern.getMiscParams().getProgramType()) << " (" << std::dec << (uint32_t)kern.getMiscParams().getProgramType() << ")" << std::endl;
fmt::print(" ProgramType: {:s} ({:d})\n", pie::hac::KernelCapabilityUtil::getProgramTypeAsString(kern.getMiscParams().getProgramType()), (uint32_t)kern.getMiscParams().getProgramType());
}
if (kern.getKernelVersion().isSet())
{
std::cout << " Kernel Version: " << std::dec << (uint32_t)kern.getKernelVersion().getVerMajor() << "." << (uint32_t)kern.getKernelVersion().getVerMinor() << std::endl;
fmt::print(" Kernel Version: {:d}.{:d}\n", kern.getKernelVersion().getVerMajor(), kern.getKernelVersion().getVerMinor());
}
if (kern.getHandleTableSize().isSet())
{
std::cout << " Handle Table Size: 0x" << std::hex << kern.getHandleTableSize().getHandleTableSize() << std::endl;
fmt::print(" Handle Table Size: 0x{:x}\n", kern.getHandleTableSize().getHandleTableSize());
}
if (kern.getMiscFlags().isSet())
{
auto misc_flags = kern.getMiscFlags().getMiscFlags();
std::cout << " Misc Flags:" << std::endl;
fmt::print(" Misc Flags:\n");
std::vector<std::string> misc_flags_names;
for (size_t misc_flags_bit = 0; misc_flags_bit < misc_flags.size(); misc_flags_bit++)
{
if (misc_flags.test(misc_flags_bit))
misc_flags_names.push_back(nn::hac::KernelCapabilityUtil::getMiscFlagsBitAsString(nn::hac::kc::MiscFlagsBit(misc_flags_bit)));
misc_flags_names.push_back(pie::hac::KernelCapabilityUtil::getMiscFlagsBitAsString(pie::hac::kc::MiscFlagsBit(misc_flags_bit)));
}
fnd::SimpleTextOutput::dumpStringList(misc_flags_names, 60, 4);
fmt::print("{:s}", tc::cli::FormatUtil::formatListWithLineLimit(misc_flags_names, 60, 4));
}
}
std::string nstool::MetaProcess::formatMappingAsString(const pie::hac::MemoryMappingHandler::sMemoryMapping& map) const
{
return fmt::format("0x{:016x} - 0x{:016x} (perm={:s}) (type={:s})", ((uint64_t)map.addr << 12), (((uint64_t)(map.addr + map.size) << 12) - 1), pie::hac::KernelCapabilityUtil::getMemoryPermissionAsString(map.perm), pie::hac::KernelCapabilityUtil::getMappingTypeAsString(map.type));
}

View file

@ -1,12 +1,10 @@
#pragma once
#include <string>
#include <fnd/types.h>
#include <fnd/IFile.h>
#include <fnd/SharedPtr.h>
#include <nn/hac/Meta.h>
#include "KeyConfiguration.h"
#include "types.h"
#include "KeyBag.h"
#include "common.h"
#include <pietendo/hac/Meta.h>
namespace nstool {
class MetaProcess
{
@ -15,32 +13,36 @@ public:
void process();
void setInputFile(const fnd::SharedPtr<fnd::IFile>& file);
void setKeyCfg(const KeyConfiguration& keycfg);
void setInputFile(const std::shared_ptr<tc::io::IStream>& file);
void setKeyCfg(const KeyBag& keycfg);
void setCliOutputMode(CliOutputMode type);
void setVerifyMode(bool verify);
const nn::hac::Meta& getMeta() const;
const pie::hac::Meta& getMeta() const;
private:
const std::string kModuleName = "MetaProcess";
std::string mModuleName;
fnd::SharedPtr<fnd::IFile> mFile;
KeyConfiguration mKeyCfg;
std::shared_ptr<tc::io::IStream> mFile;
KeyBag mKeyCfg;
CliOutputMode mCliOutputMode;
bool mVerify;
nn::hac::Meta mMeta;
pie::hac::Meta mMeta;
void importMeta();
void validateAcidSignature(const nn::hac::AccessControlInfoDesc& acid, byte_t key_generation);
void validateAciFromAcid(const nn::hac::AccessControlInfo& aci, const nn::hac::AccessControlInfoDesc& acid);
void validateAcidSignature(const pie::hac::AccessControlInfoDesc& acid, byte_t key_generation);
void validateAciFromAcid(const pie::hac::AccessControlInfo& aci, const pie::hac::AccessControlInfoDesc& acid);
void displayMetaHeader(const nn::hac::Meta& hdr);
void displayAciHdr(const nn::hac::AccessControlInfo& aci);
void displayAciDescHdr(const nn::hac::AccessControlInfoDesc& aci);
void displayFac(const nn::hac::FileSystemAccessControl& fac);
void displaySac(const nn::hac::ServiceAccessControl& sac);
void displayKernelCap(const nn::hac::KernelCapabilityControl& kern);
};
void displayMetaHeader(const pie::hac::Meta& hdr);
void displayAciHdr(const pie::hac::AccessControlInfo& aci);
void displayAciDescHdr(const pie::hac::AccessControlInfoDesc& aci);
void displayFac(const pie::hac::FileSystemAccessControl& fac);
void displaySac(const pie::hac::ServiceAccessControl& sac);
void displayKernelCap(const pie::hac::KernelCapabilityControl& kern);
std::string formatMappingAsString(const pie::hac::MemoryMappingHandler::sMemoryMapping& map) const;
};
}

View file

@ -1,525 +1,530 @@
#include "NacpProcess.h"
#include <sstream>
#include <iostream>
#include <iomanip>
#include <pietendo/hac/ApplicationControlPropertyUtil.h>
#include <fnd/SimpleTextOutput.h>
#include <fnd/OffsetAdjustedIFile.h>
#include <nn/hac/ApplicationControlPropertyUtil.h>
NacpProcess::NacpProcess() :
nstool::NacpProcess::NacpProcess() :
mModuleName("nstool::NacpProcess"),
mFile(),
mCliOutputMode(_BIT(OUTPUT_BASIC)),
mCliOutputMode(true, false, false, false),
mVerify(false)
{
}
void NacpProcess::process()
void nstool::NacpProcess::process()
{
importNacp();
if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
if (mCliOutputMode.show_basic_info)
displayNacp();
}
void NacpProcess::setInputFile(const fnd::SharedPtr<fnd::IFile>& file)
void nstool::NacpProcess::setInputFile(const std::shared_ptr<tc::io::IStream>& file)
{
mFile = file;
}
void NacpProcess::setCliOutputMode(CliOutputMode type)
void nstool::NacpProcess::setCliOutputMode(CliOutputMode type)
{
mCliOutputMode = type;
}
void NacpProcess::setVerifyMode(bool verify)
void nstool::NacpProcess::setVerifyMode(bool verify)
{
mVerify = verify;
}
const nn::hac::ApplicationControlProperty& NacpProcess::getApplicationControlProperty() const
const pie::hac::ApplicationControlProperty& nstool::NacpProcess::getApplicationControlProperty() const
{
return mNacp;
}
void NacpProcess::importNacp()
void nstool::NacpProcess::importNacp()
{
fnd::Vec<byte_t> scratch;
if (*mFile == nullptr)
if (mFile == nullptr)
{
throw fnd::Exception(kModuleName, "No file reader set.");
throw tc::Exception(mModuleName, "No file reader set.");
}
if (mFile->canRead() == false || mFile->canSeek() == false)
{
throw tc::NotSupportedException(mModuleName, "Input stream requires read/seek permissions.");
}
scratch.alloc((*mFile)->size());
(*mFile)->read(scratch.data(), 0, scratch.size());
// check if file_size does matches expected size
size_t file_size = tc::io::IOUtil::castInt64ToSize(mFile->length());
if (file_size != sizeof(pie::hac::sApplicationControlProperty))
{
throw tc::Exception(mModuleName, "File was incorrect size.");
}
// read cnmt
tc::ByteData scratch = tc::ByteData(file_size);
mFile->seek(0, tc::io::SeekOrigin::Begin);
mFile->read(scratch.data(), scratch.size());
mNacp.fromBytes(scratch.data(), scratch.size());
}
void NacpProcess::displayNacp()
void nstool::NacpProcess::displayNacp()
{
std::cout << "[ApplicationControlProperty]" << std::endl;
fmt::print("[ApplicationControlProperty]\n");
// Title
if (mNacp.getTitle().size() > 0)
{
std::cout << " Title:" << std::endl;
fmt::print(" Title:\n");
for (auto itr = mNacp.getTitle().begin(); itr != mNacp.getTitle().end(); itr++)
{
std::cout << " " << nn::hac::ApplicationControlPropertyUtil::getLanguageAsString(itr->language) << ":" << std::endl;
std::cout << " Name: " << itr->name << std::endl;
std::cout << " Publisher: " << itr->publisher << std::endl;
fmt::print(" {:s}:\n", pie::hac::ApplicationControlPropertyUtil::getLanguageAsString(itr->language));
fmt::print(" Name: {:s}\n", itr->name);
fmt::print(" Publisher: {:s}\n", itr->publisher);
}
}
else if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
else if (mCliOutputMode.show_extended_info)
{
std::cout << " Title: None" << std::endl;
fmt::print(" Title: None\n");
}
// Isbn
if (mNacp.getIsbn().empty() == false)
{
std::cout << " ISBN: " << mNacp.getIsbn() << std::endl;
fmt::print(" ISBN: {:s}\n", mNacp.getIsbn());
}
else if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
else if (mCliOutputMode.show_extended_info)
{
std::cout << " ISBN: (NotSet)" << std::endl;
fmt::print(" ISBN: (NotSet)\n");
}
// StartupUserAccount
if (mNacp.getStartupUserAccount() != nn::hac::nacp::StartupUserAccount::None || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mNacp.getStartupUserAccount() != pie::hac::nacp::StartupUserAccount_None || mCliOutputMode.show_extended_info)
{
std::cout << " StartupUserAccount: " << nn::hac::ApplicationControlPropertyUtil::getStartupUserAccountAsString(mNacp.getStartupUserAccount()) << std::endl;
fmt::print(" StartupUserAccount: {:s}\n", pie::hac::ApplicationControlPropertyUtil::getStartupUserAccountAsString(mNacp.getStartupUserAccount()));
}
// UserAccountSwitchLock
if (mNacp.getUserAccountSwitchLock() != nn::hac::nacp::UserAccountSwitchLock::Disable || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mNacp.getUserAccountSwitchLock() != pie::hac::nacp::UserAccountSwitchLock_Disable || mCliOutputMode.show_extended_info)
{
std::cout << " UserAccountSwitchLock: " << nn::hac::ApplicationControlPropertyUtil::getUserAccountSwitchLockAsString(mNacp.getUserAccountSwitchLock()) << std::endl;
fmt::print(" UserAccountSwitchLock: {:s}\n", pie::hac::ApplicationControlPropertyUtil::getUserAccountSwitchLockAsString(mNacp.getUserAccountSwitchLock()));
}
// AddOnContentRegistrationType
if (mNacp.getAddOnContentRegistrationType() != nn::hac::nacp::AddOnContentRegistrationType::AllOnLaunch || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mNacp.getAddOnContentRegistrationType() != pie::hac::nacp::AddOnContentRegistrationType_AllOnLaunch || mCliOutputMode.show_extended_info)
{
std::cout << " AddOnContentRegistrationType: " << nn::hac::ApplicationControlPropertyUtil::getAddOnContentRegistrationTypeAsString(mNacp.getAddOnContentRegistrationType()) << std::endl;
fmt::print(" AddOnContentRegistrationType: {:s}\n", pie::hac::ApplicationControlPropertyUtil::getAddOnContentRegistrationTypeAsString(mNacp.getAddOnContentRegistrationType()));
}
// Attribute
if (mNacp.getAttribute().size() > 0)
{
std::cout << " Attribute:" << std::endl;
fmt::print(" Attribute:\n");
for (auto itr = mNacp.getAttribute().begin(); itr != mNacp.getAttribute().end(); itr++)
{
std::cout << " " << nn::hac::ApplicationControlPropertyUtil::getAttributeFlagAsString(*itr) << std::endl;
fmt::print(" {:s}\n", pie::hac::ApplicationControlPropertyUtil::getAttributeFlagAsString(*itr));
}
}
else if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
else if (mCliOutputMode.show_extended_info)
{
std::cout << " Attribute: None" << std::endl;
fmt::print(" Attribute: None\n");
}
// SupportedLanguage
if (mNacp.getSupportedLanguage().size() > 0)
{
std::cout << " SupportedLanguage:" << std::endl;
fmt::print(" SupportedLanguage:\n");
for (auto itr = mNacp.getSupportedLanguage().begin(); itr != mNacp.getSupportedLanguage().end(); itr++)
{
std::cout << " " << nn::hac::ApplicationControlPropertyUtil::getLanguageAsString(*itr) << std::endl;
fmt::print(" {:s}\n", pie::hac::ApplicationControlPropertyUtil::getLanguageAsString(*itr));
}
}
else if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
else if (mCliOutputMode.show_extended_info)
{
std::cout << " SupportedLanguage: None" << std::endl;
fmt::print(" SupportedLanguage: None\n");
}
// ParentalControl
if (mNacp.getParentalControl().size() > 0)
{
std::cout << " ParentalControl:" << std::endl;
fmt::print(" ParentalControl:\n");
for (auto itr = mNacp.getParentalControl().begin(); itr != mNacp.getParentalControl().end(); itr++)
{
std::cout << " " << nn::hac::ApplicationControlPropertyUtil::getParentalControlFlagAsString(*itr) << std::endl;
fmt::print(" {:s}\n", pie::hac::ApplicationControlPropertyUtil::getParentalControlFlagAsString(*itr));
}
}
else if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
else if (mCliOutputMode.show_extended_info)
{
std::cout << " ParentalControl: None" << std::endl;
fmt::print(" ParentalControl: None\n");
}
// Screenshot
if (mNacp.getScreenshot() != nn::hac::nacp::Screenshot::Allow || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mNacp.getScreenshot() != pie::hac::nacp::Screenshot_Allow || mCliOutputMode.show_extended_info)
{
std::cout << " Screenshot: " << nn::hac::ApplicationControlPropertyUtil::getScreenshotAsString(mNacp.getScreenshot()) << std::endl;
fmt::print(" Screenshot: {:s}\n", pie::hac::ApplicationControlPropertyUtil::getScreenshotAsString(mNacp.getScreenshot()));
}
// VideoCapture
if (mNacp.getVideoCapture() != nn::hac::nacp::VideoCapture::Disable || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mNacp.getVideoCapture() != pie::hac::nacp::VideoCapture_Disable || mCliOutputMode.show_extended_info)
{
std::cout << " VideoCapture: " << nn::hac::ApplicationControlPropertyUtil::getVideoCaptureAsString(mNacp.getVideoCapture()) << std::endl;
fmt::print(" VideoCapture: {:s}\n", pie::hac::ApplicationControlPropertyUtil::getVideoCaptureAsString(mNacp.getVideoCapture()));
}
// DataLossConfirmation
if (mNacp.getDataLossConfirmation() != nn::hac::nacp::DataLossConfirmation::None || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mNacp.getDataLossConfirmation() != pie::hac::nacp::DataLossConfirmation_None || mCliOutputMode.show_extended_info)
{
std::cout << " DataLossConfirmation: " << nn::hac::ApplicationControlPropertyUtil::getDataLossConfirmationAsString(mNacp.getDataLossConfirmation()) << std::endl;
fmt::print(" DataLossConfirmation: {:s}\n", pie::hac::ApplicationControlPropertyUtil::getDataLossConfirmationAsString(mNacp.getDataLossConfirmation()));
}
// PlayLogPolicy
if (mNacp.getPlayLogPolicy() != nn::hac::nacp::PlayLogPolicy::All || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mNacp.getPlayLogPolicy() != pie::hac::nacp::PlayLogPolicy_All || mCliOutputMode.show_extended_info)
{
std::cout << " PlayLogPolicy: " << nn::hac::ApplicationControlPropertyUtil::getPlayLogPolicyAsString(mNacp.getPlayLogPolicy()) << std::endl;
fmt::print(" PlayLogPolicy: {:s}\n", pie::hac::ApplicationControlPropertyUtil::getPlayLogPolicyAsString(mNacp.getPlayLogPolicy()));
}
// PresenceGroupId
if (mNacp.getPresenceGroupId() != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mNacp.getPresenceGroupId() != 0 || mCliOutputMode.show_extended_info)
{
std::cout << " PresenceGroupId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mNacp.getPresenceGroupId() << std::endl;
fmt::print(" PresenceGroupId: 0x{:016x}\n", mNacp.getPresenceGroupId());
}
// RatingAge
if (mNacp.getRatingAge().size() > 0)
{
std::cout << " RatingAge:" << std::endl;
fmt::print(" RatingAge:\n");
for (auto itr = mNacp.getRatingAge().begin(); itr != mNacp.getRatingAge().end(); itr++)
{
std::cout << " " << nn::hac::ApplicationControlPropertyUtil::getOrganisationAsString(itr->organisation) << ":" << std::endl;
std::cout << " Age: " << std::dec << (uint32_t)itr->age << std::endl;
fmt::print(" {:s}:\n", pie::hac::ApplicationControlPropertyUtil::getOrganisationAsString(itr->organisation));
fmt::print(" Age: {:d}\n", itr->age);
}
}
else if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
else if (mCliOutputMode.show_extended_info)
{
std::cout << " RatingAge: None" << std::endl;
fmt::print(" RatingAge: None\n");
}
// DisplayVersion
if (mNacp.getDisplayVersion().empty() == false)
{
std::cout << " DisplayVersion: " << mNacp.getDisplayVersion() << std::endl;
fmt::print(" DisplayVersion: {:s}\n", mNacp.getDisplayVersion());
}
else if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
else if (mCliOutputMode.show_extended_info)
{
std::cout << " DisplayVersion: (NotSet)" << std::endl;
fmt::print(" DisplayVersion: (NotSet)\n");
}
// AddOnContentBaseId
if (mNacp.getAddOnContentBaseId() != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mNacp.getAddOnContentBaseId() != 0 || mCliOutputMode.show_extended_info)
{
std::cout << " AddOnContentBaseId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mNacp.getAddOnContentBaseId() << std::endl;
fmt::print(" AddOnContentBaseId: 0x{:016x}\n", mNacp.getAddOnContentBaseId());
}
// SaveDataOwnerId
if (mNacp.getSaveDataOwnerId() != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mNacp.getSaveDataOwnerId() != 0 || mCliOutputMode.show_extended_info)
{
std::cout << " SaveDataOwnerId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mNacp.getSaveDataOwnerId() << std::endl;
fmt::print(" SaveDataOwnerId: 0x{:016x}\n", mNacp.getSaveDataOwnerId());
}
// UserAccountSaveDataSize
if (mNacp.getUserAccountSaveDataSize().size != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mNacp.getUserAccountSaveDataSize().size != 0 || mCliOutputMode.show_extended_info)
{
std::cout << " UserAccountSaveDataSize: " << nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getUserAccountSaveDataSize().size) << std::endl;
fmt::print(" UserAccountSaveDataSize: {:s}\n", pie::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getUserAccountSaveDataSize().size));
}
// UserAccountSaveDataJournalSize
if (mNacp.getUserAccountSaveDataSize().journal_size != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mNacp.getUserAccountSaveDataSize().journal_size != 0 || mCliOutputMode.show_extended_info)
{
std::cout << " UserAccountSaveDataJournalSize: " << nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getUserAccountSaveDataSize().journal_size) << std::endl;
fmt::print(" UserAccountSaveDataJournalSize: {:s}\n", pie::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getUserAccountSaveDataSize().journal_size));
}
// DeviceSaveDataSize
if (mNacp.getDeviceSaveDataSize().size != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mNacp.getDeviceSaveDataSize().size != 0 || mCliOutputMode.show_extended_info)
{
std::cout << " DeviceSaveDataSize: " << nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getDeviceSaveDataSize().size) << std::endl;
fmt::print(" DeviceSaveDataSize: {:s}\n", pie::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getDeviceSaveDataSize().size));
}
// DeviceSaveDataJournalSize
if (mNacp.getDeviceSaveDataSize().journal_size != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mNacp.getDeviceSaveDataSize().journal_size != 0 || mCliOutputMode.show_extended_info)
{
std::cout << " DeviceSaveDataJournalSize: " << nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getDeviceSaveDataSize().journal_size) << std::endl;
fmt::print(" DeviceSaveDataJournalSize: {:s}\n", pie::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getDeviceSaveDataSize().journal_size));
}
// BcatDeliveryCacheStorageSize
if (mNacp.getBcatDeliveryCacheStorageSize() != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mNacp.getBcatDeliveryCacheStorageSize() != 0 || mCliOutputMode.show_extended_info)
{
std::cout << " BcatDeliveryCacheStorageSize: " << nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getBcatDeliveryCacheStorageSize()) << std::endl;
fmt::print(" BcatDeliveryCacheStorageSize: {:s}\n", pie::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getBcatDeliveryCacheStorageSize()));
}
// ApplicationErrorCodeCategory
if (mNacp.getApplicationErrorCodeCategory().empty() == false)
{
std::cout << " ApplicationErrorCodeCategory: " << mNacp.getApplicationErrorCodeCategory() << std::endl;
fmt::print(" ApplicationErrorCodeCategory: {:s}\n", mNacp.getApplicationErrorCodeCategory());
}
else if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
else if (mCliOutputMode.show_extended_info)
{
std::cout << " ApplicationErrorCodeCategory: (NotSet)" << std::endl;
fmt::print(" ApplicationErrorCodeCategory: (NotSet)\n");
}
// LocalCommunicationId
if (mNacp.getLocalCommunicationId().size() > 0)
{
std::cout << " LocalCommunicationId:" << std::endl;
fmt::print(" LocalCommunicationId:\n");
for (auto itr = mNacp.getLocalCommunicationId().begin(); itr != mNacp.getLocalCommunicationId().end(); itr++)
{
std::cout << " 0x" << std::hex << std::setw(16) << std::setfill('0') << *itr << std::endl;
fmt::print(" 0x{:016x}\n", *itr);
}
}
else if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
else if (mCliOutputMode.show_extended_info)
{
std::cout << " LocalCommunicationId: None" << std::endl;
fmt::print(" LocalCommunicationId: None\n");
}
// LogoType
//if (mNacp.getLogoType() != nn::hac::nacp::LogoType::Nintendo || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
//if (mNacp.getLogoType() != pie::hac::nacp::LogoType_Nintendo || mCliOutputMode.show_extended_info)
//{
std::cout << " LogoType: " << nn::hac::ApplicationControlPropertyUtil::getLogoTypeAsString(mNacp.getLogoType()) << std::endl;
fmt::print(" LogoType: {:s}\n", pie::hac::ApplicationControlPropertyUtil::getLogoTypeAsString(mNacp.getLogoType()));
//}
// LogoHandling
if (mNacp.getLogoHandling() != nn::hac::nacp::LogoHandling::Auto || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mNacp.getLogoHandling() != pie::hac::nacp::LogoHandling_Auto || mCliOutputMode.show_extended_info)
{
std::cout << " LogoHandling: " << nn::hac::ApplicationControlPropertyUtil::getLogoHandlingAsString(mNacp.getLogoHandling()) << std::endl;
fmt::print(" LogoHandling: {:s}\n", pie::hac::ApplicationControlPropertyUtil::getLogoHandlingAsString(mNacp.getLogoHandling()));
}
// RuntimeAddOnContentInstall
if (mNacp.getRuntimeAddOnContentInstall() != nn::hac::nacp::RuntimeAddOnContentInstall::Deny || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mNacp.getRuntimeAddOnContentInstall() != pie::hac::nacp::RuntimeAddOnContentInstall_Deny || mCliOutputMode.show_extended_info)
{
std::cout << " RuntimeAddOnContentInstall: " << nn::hac::ApplicationControlPropertyUtil::getRuntimeAddOnContentInstallAsString(mNacp.getRuntimeAddOnContentInstall()) << std::endl;
fmt::print(" RuntimeAddOnContentInstall: {:s}\n", pie::hac::ApplicationControlPropertyUtil::getRuntimeAddOnContentInstallAsString(mNacp.getRuntimeAddOnContentInstall()));
}
// RuntimeParameterDelivery
if (mNacp.getRuntimeParameterDelivery() != nn::hac::nacp::RuntimeParameterDelivery::Always || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mNacp.getRuntimeParameterDelivery() != pie::hac::nacp::RuntimeParameterDelivery_Always || mCliOutputMode.show_extended_info)
{
std::cout << " RuntimeParameterDelivery: " << nn::hac::ApplicationControlPropertyUtil::getRuntimeParameterDeliveryAsString(mNacp.getRuntimeParameterDelivery()) << std::endl;
fmt::print(" RuntimeParameterDelivery: {:s}\n", pie::hac::ApplicationControlPropertyUtil::getRuntimeParameterDeliveryAsString(mNacp.getRuntimeParameterDelivery()));
}
// CrashReport
if (mNacp.getCrashReport() != nn::hac::nacp::CrashReport::Deny || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mNacp.getCrashReport() != pie::hac::nacp::CrashReport_Deny || mCliOutputMode.show_extended_info)
{
std::cout << " CrashReport: " << nn::hac::ApplicationControlPropertyUtil::getCrashReportAsString(mNacp.getCrashReport()) << std::endl;
fmt::print(" CrashReport: {:s}\n", pie::hac::ApplicationControlPropertyUtil::getCrashReportAsString(mNacp.getCrashReport()));
}
// Hdcp
if (mNacp.getHdcp() != nn::hac::nacp::Hdcp::None || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mNacp.getHdcp() != pie::hac::nacp::Hdcp_None || mCliOutputMode.show_extended_info)
{
std::cout << " Hdcp: " << nn::hac::ApplicationControlPropertyUtil::getHdcpAsString(mNacp.getHdcp()) << std::endl;
fmt::print(" Hdcp: {:s}\n", pie::hac::ApplicationControlPropertyUtil::getHdcpAsString(mNacp.getHdcp()));
}
// SeedForPsuedoDeviceId
if (mNacp.getSeedForPsuedoDeviceId() != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mNacp.getSeedForPsuedoDeviceId() != 0 || mCliOutputMode.show_extended_info)
{
std::cout << " SeedForPsuedoDeviceId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mNacp.getSeedForPsuedoDeviceId() << std::endl;
fmt::print(" SeedForPsuedoDeviceId: 0x{:016x}\n", mNacp.getSeedForPsuedoDeviceId());
}
// BcatPassphase
if (mNacp.getBcatPassphase().empty() == false)
{
std::cout << " BcatPassphase: " << mNacp.getBcatPassphase() << std::endl;
fmt::print(" BcatPassphase: {:s}\n", mNacp.getBcatPassphase());
}
else if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
else if (mCliOutputMode.show_extended_info)
{
std::cout << " BcatPassphase: (NotSet)" << std::endl;
fmt::print(" BcatPassphase: (NotSet)\n");
}
// StartupUserAccountOption
if (mNacp.getStartupUserAccountOption().size() > 0)
{
std::cout << " StartupUserAccountOption:" << std::endl;
fmt::print(" StartupUserAccountOption:\n");
for (auto itr = mNacp.getStartupUserAccountOption().begin(); itr != mNacp.getStartupUserAccountOption().end(); itr++)
{
std::cout << " " << nn::hac::ApplicationControlPropertyUtil::getStartupUserAccountOptionFlagAsString(*itr) << std::endl;
fmt::print(" {:s}\n", pie::hac::ApplicationControlPropertyUtil::getStartupUserAccountOptionFlagAsString(*itr));
}
}
else if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
else if (mCliOutputMode.show_extended_info)
{
std::cout << " StartupUserAccountOption: None" << std::endl;
fmt::print(" StartupUserAccountOption: None\n");
}
// UserAccountSaveDataSizeMax
if (mNacp.getUserAccountSaveDataMax().size != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mNacp.getUserAccountSaveDataMax().size != 0 || mCliOutputMode.show_extended_info)
{
std::cout << " UserAccountSaveDataSizeMax: " << nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getUserAccountSaveDataMax().size) << std::endl;
fmt::print(" UserAccountSaveDataSizeMax: {:s}\n", pie::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getUserAccountSaveDataMax().size));
}
// UserAccountSaveDataJournalSizeMax
if (mNacp.getUserAccountSaveDataMax().journal_size != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mNacp.getUserAccountSaveDataMax().journal_size != 0 || mCliOutputMode.show_extended_info)
{
std::cout << " UserAccountSaveDataJournalSizeMax: " << nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getUserAccountSaveDataMax().journal_size) << std::endl;
fmt::print(" UserAccountSaveDataJournalSizeMax: {:s}\n", pie::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getUserAccountSaveDataMax().journal_size));
}
// DeviceSaveDataSizeMax
if (mNacp.getDeviceSaveDataMax().size != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mNacp.getDeviceSaveDataMax().size != 0 || mCliOutputMode.show_extended_info)
{
std::cout << " DeviceSaveDataSizeMax: " << nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getDeviceSaveDataMax().size) << std::endl;
fmt::print(" DeviceSaveDataSizeMax: {:s}\n", pie::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getDeviceSaveDataMax().size));
}
// DeviceSaveDataJournalSizeMax
if (mNacp.getDeviceSaveDataMax().journal_size != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mNacp.getDeviceSaveDataMax().journal_size != 0 || mCliOutputMode.show_extended_info)
{
std::cout << " DeviceSaveDataJournalSizeMax: " << nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getDeviceSaveDataMax().journal_size) << std::endl;
fmt::print(" DeviceSaveDataJournalSizeMax: {:s}\n", pie::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getDeviceSaveDataMax().journal_size));
}
// TemporaryStorageSize
if (mNacp.getTemporaryStorageSize() != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mNacp.getTemporaryStorageSize() != 0 || mCliOutputMode.show_extended_info)
{
std::cout << " TemporaryStorageSize: " << nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getTemporaryStorageSize()) << std::endl;
fmt::print(" TemporaryStorageSize: {:s}\n", pie::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getTemporaryStorageSize()));
}
// CacheStorageSize
if (mNacp.getCacheStorageSize().size != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mNacp.getCacheStorageSize().size != 0 || mCliOutputMode.show_extended_info)
{
std::cout << " CacheStorageSize: " << nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getCacheStorageSize().size) << std::endl;
fmt::print(" CacheStorageSize: {:s}\n", pie::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getCacheStorageSize().size));
}
// CacheStorageJournalSize
if (mNacp.getCacheStorageSize().journal_size != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mNacp.getCacheStorageSize().journal_size != 0 || mCliOutputMode.show_extended_info)
{
std::cout << " CacheStorageJournalSize: " << nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getCacheStorageSize().journal_size) << std::endl;
fmt::print(" CacheStorageJournalSize: {:s}\n", pie::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getCacheStorageSize().journal_size));
}
// CacheStorageDataAndJournalSizeMax
if (mNacp.getCacheStorageDataAndJournalSizeMax() != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mNacp.getCacheStorageDataAndJournalSizeMax() != 0 || mCliOutputMode.show_extended_info)
{
std::cout << " CacheStorageDataAndJournalSizeMax: " << nn::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getCacheStorageDataAndJournalSizeMax()) << std::endl;
fmt::print(" CacheStorageDataAndJournalSizeMax: {:s}\n", pie::hac::ApplicationControlPropertyUtil::getSaveDataSizeAsString(mNacp.getCacheStorageDataAndJournalSizeMax()));
}
// CacheStorageIndexMax
if (mNacp.getCacheStorageIndexMax() != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mNacp.getCacheStorageIndexMax() != 0 || mCliOutputMode.show_extended_info)
{
std::cout << " CacheStorageIndexMax: 0x" << std::hex << std::setw(4) << std::setfill('0') << mNacp.getCacheStorageIndexMax() << std::endl;
fmt::print(" CacheStorageIndexMax: 0x{:04x}\n", mNacp.getCacheStorageIndexMax());
}
// PlayLogQueryableApplicationId
if (mNacp.getPlayLogQueryableApplicationId().size() > 0)
{
std::cout << " PlayLogQueryableApplicationId:" << std::endl;
fmt::print(" PlayLogQueryableApplicationId:\n");
for (auto itr = mNacp.getPlayLogQueryableApplicationId().begin(); itr != mNacp.getPlayLogQueryableApplicationId().end(); itr++)
{
std::cout << " 0x" << std::hex << std::setw(16) << std::setfill('0') << *itr << std::endl;
fmt::print(" 0x{:016x}\n", *itr);
}
}
else if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
else if (mCliOutputMode.show_extended_info)
{
std::cout << " PlayLogQueryableApplicationId: None" << std::endl;
fmt::print(" PlayLogQueryableApplicationId: None\n");
}
// PlayLogQueryCapability
if (mNacp.getPlayLogQueryCapability() != nn::hac::nacp::PlayLogQueryCapability::None || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mNacp.getPlayLogQueryCapability() != pie::hac::nacp::PlayLogQueryCapability_None || mCliOutputMode.show_extended_info)
{
std::cout << " PlayLogQueryCapability: " << nn::hac::ApplicationControlPropertyUtil::getPlayLogQueryCapabilityAsString(mNacp.getPlayLogQueryCapability()) << std::endl;
fmt::print(" PlayLogQueryCapability: {:s}\n", pie::hac::ApplicationControlPropertyUtil::getPlayLogQueryCapabilityAsString(mNacp.getPlayLogQueryCapability()));
}
// Repair
if (mNacp.getRepair().size() > 0)
{
std::cout << " Repair:" << std::endl;
fmt::print(" Repair:\n");
for (auto itr = mNacp.getRepair().begin(); itr != mNacp.getRepair().end(); itr++)
{
std::cout << " " << nn::hac::ApplicationControlPropertyUtil::getRepairFlagAsString(*itr) << std::endl;
fmt::print(" {:s}\n", pie::hac::ApplicationControlPropertyUtil::getRepairFlagAsString(*itr));
}
}
else if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
else if (mCliOutputMode.show_extended_info)
{
std::cout << " Repair: None" << std::endl;
fmt::print(" Repair: None\n");
}
// ProgramIndex
if (mNacp.getProgramIndex() != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mNacp.getProgramIndex() != 0 || mCliOutputMode.show_extended_info)
{
std::cout << " ProgramIndex: 0x" << std::hex << std::setw(2) << std::setfill('0') << (uint32_t)mNacp.getProgramIndex() << std::endl;
fmt::print(" ProgramIndex: 0x{:02x}\n", mNacp.getProgramIndex());
}
// RequiredNetworkServiceLicenseOnLaunch
if (mNacp.getRequiredNetworkServiceLicenseOnLaunch().size() > 0)
{
std::cout << " RequiredNetworkServiceLicenseOnLaunch:" << std::endl;
fmt::print(" RequiredNetworkServiceLicenseOnLaunch:\n");
for (auto itr = mNacp.getRequiredNetworkServiceLicenseOnLaunch().begin(); itr != mNacp.getRequiredNetworkServiceLicenseOnLaunch().end(); itr++)
{
std::cout << " " << nn::hac::ApplicationControlPropertyUtil::getRequiredNetworkServiceLicenseOnLaunchFlagAsString(*itr) << std::endl;
fmt::print(" {:s}\n", pie::hac::ApplicationControlPropertyUtil::getRequiredNetworkServiceLicenseOnLaunchFlagAsString(*itr));
}
}
else if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
else if (mCliOutputMode.show_extended_info)
{
std::cout << " RequiredNetworkServiceLicenseOnLaunch: None" << std::endl;
fmt::print(" RequiredNetworkServiceLicenseOnLaunch: None\n");
}
// NeighborDetectionClientConfiguration
auto detect_config = mNacp.getNeighborDetectionClientConfiguration();
if (detect_config.countSendGroupConfig() > 0 || detect_config.countReceivableGroupConfig() > 0)
{
std::cout << " NeighborDetectionClientConfiguration:" << std::endl;
fmt::print(" NeighborDetectionClientConfiguration:\n");
if (detect_config.countSendGroupConfig() > 0)
{
std::cout << " SendGroupConfig:" << std::endl;
std::cout << " GroupId: 0x" << std::hex << std::setw(16) << std::setfill('0') << detect_config.send_data_configuration.group_id << std::endl;
std::cout << " Key: " << fnd::SimpleTextOutput::arrayToString(detect_config.send_data_configuration.key, nn::hac::nacp::kNeighborDetectionGroupConfigurationKeyLength, false, "") << std::endl;
fmt::print(" SendGroupConfig:\n");
fmt::print(" GroupId: 0x{:016x}\n", detect_config.send_data_configuration.group_id);
fmt::print(" Key: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(detect_config.send_data_configuration.key.data(), detect_config.send_data_configuration.key.size(), false, ""));
}
else if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
else if (mCliOutputMode.show_extended_info)
{
std::cout << " SendGroupConfig: None" << std::endl;
fmt::print(" SendGroupConfig: None\n");
}
if (detect_config.countReceivableGroupConfig() > 0)
{
std::cout << " ReceivableGroupConfig:" << std::endl;
for (size_t i = 0; i < nn::hac::nacp::kReceivableGroupConfigurationCount; i++)
fmt::print(" ReceivableGroupConfig:\n");
for (size_t i = 0; i < pie::hac::nacp::kReceivableGroupConfigurationCount; i++)
{
if (detect_config.receivable_data_configuration[i].isNull())
continue;
std::cout << " GroupId: 0x" << std::hex << std::setw(16) << std::setfill('0') << detect_config.receivable_data_configuration[i].group_id << std::endl;
std::cout << " Key: " << fnd::SimpleTextOutput::arrayToString(detect_config.receivable_data_configuration[i].key, nn::hac::nacp::kNeighborDetectionGroupConfigurationKeyLength, false, "") << std::endl;
fmt::print(" GroupId: 0x{:016x}\n", detect_config.receivable_data_configuration[i].group_id);
fmt::print(" Key: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(detect_config.receivable_data_configuration[i].key.data(), detect_config.receivable_data_configuration[i].key.size(), false, ""));
}
}
else if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
else if (mCliOutputMode.show_extended_info)
{
std::cout << " ReceivableGroupConfig: None" << std::endl;
fmt::print(" ReceivableGroupConfig: None\n");
}
}
else if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
else if (mCliOutputMode.show_extended_info)
{
std::cout << " NeighborDetectionClientConfiguration: None" << std::endl;
fmt::print(" NeighborDetectionClientConfiguration: None\n");
}
// JitConfiguration
if (mNacp.getJitConfiguration().is_enabled || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mNacp.getJitConfiguration().is_enabled || mCliOutputMode.show_extended_info)
{
std::cout << " JitConfiguration:" << std::endl;
std::cout << " IsEnabled: " << std::boolalpha << mNacp.getJitConfiguration().is_enabled << std::endl;
std::cout << " MemorySize: 0x" << std::hex << std::setw(16) << std::setfill('0') << mNacp.getJitConfiguration().memory_size << std::endl;
fmt::print(" JitConfiguration:\n");
fmt::print(" IsEnabled: {}\n", mNacp.getJitConfiguration().is_enabled);
fmt::print(" MemorySize: 0x{:016x}\n", mNacp.getJitConfiguration().memory_size);
}
// PlayReportPermission
if (mNacp.getPlayReportPermission() != nn::hac::nacp::PlayReportPermission::None || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mNacp.getPlayReportPermission() != pie::hac::nacp::PlayReportPermission_None || mCliOutputMode.show_extended_info)
{
std::cout << " PlayReportPermission: " << nn::hac::ApplicationControlPropertyUtil::getPlayReportPermissionAsString(mNacp.getPlayReportPermission()) << std::endl;
fmt::print(" PlayReportPermission: {:s}\n", pie::hac::ApplicationControlPropertyUtil::getPlayReportPermissionAsString(mNacp.getPlayReportPermission()));
}
// CrashScreenshotForProd
if (mNacp.getCrashScreenshotForProd() != nn::hac::nacp::CrashScreenshotForProd::Deny || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mNacp.getCrashScreenshotForProd() != pie::hac::nacp::CrashScreenshotForProd_Deny || mCliOutputMode.show_extended_info)
{
std::cout << " CrashScreenshotForProd: " << nn::hac::ApplicationControlPropertyUtil::getCrashScreenshotForProdAsString(mNacp.getCrashScreenshotForProd()) << std::endl;
fmt::print(" CrashScreenshotForProd: {:s}\n", pie::hac::ApplicationControlPropertyUtil::getCrashScreenshotForProdAsString(mNacp.getCrashScreenshotForProd()));
}
// CrashScreenshotForDev
if (mNacp.getCrashScreenshotForDev() != nn::hac::nacp::CrashScreenshotForDev::Deny || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mNacp.getCrashScreenshotForDev() != pie::hac::nacp::CrashScreenshotForDev_Deny || mCliOutputMode.show_extended_info)
{
std::cout << " CrashScreenshotForDev: " << nn::hac::ApplicationControlPropertyUtil::getCrashScreenshotForDevAsString(mNacp.getCrashScreenshotForDev()) << std::endl;
fmt::print(" CrashScreenshotForDev: {:s}\n", pie::hac::ApplicationControlPropertyUtil::getCrashScreenshotForDevAsString(mNacp.getCrashScreenshotForDev()));
}
// AccessibleLaunchRequiredVersion
if (mNacp.getAccessibleLaunchRequiredVersionApplicationId().size() > 0)
{
std::cout << " AccessibleLaunchRequiredVersion:" << std::endl;
std::cout << " ApplicationId:" << std::endl;
fmt::print(" AccessibleLaunchRequiredVersion:\n");
fmt::print(" ApplicationId:\n");
for (auto itr = mNacp.getAccessibleLaunchRequiredVersionApplicationId().begin(); itr != mNacp.getAccessibleLaunchRequiredVersionApplicationId().end(); itr++)
{
std::cout << " 0x" << std::hex << std::setw(16) << std::setfill('0') << *itr << std::endl;
fmt::print(" 0x{:016x}\n", *itr);
}
}
else if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
else if (mCliOutputMode.show_extended_info)
{
std::cout << " AccessibleLaunchRequiredVersion: None" << std::endl;
fmt::print(" AccessibleLaunchRequiredVersion: None\n");
}
}

View file

@ -1,11 +1,9 @@
#pragma once
#include <string>
#include <fnd/types.h>
#include <fnd/IFile.h>
#include <fnd/SharedPtr.h>
#include <nn/hac/ApplicationControlProperty.h>
#include "types.h"
#include "common.h"
#include <pietendo/hac/ApplicationControlProperty.h>
namespace nstool {
class NacpProcess
{
@ -14,21 +12,23 @@ public:
void process();
void setInputFile(const fnd::SharedPtr<fnd::IFile>& file);
void setInputFile(const std::shared_ptr<tc::io::IStream>& file);
void setCliOutputMode(CliOutputMode type);
void setVerifyMode(bool verify);
const nn::hac::ApplicationControlProperty& getApplicationControlProperty() const;
const pie::hac::ApplicationControlProperty& getApplicationControlProperty() const;
private:
const std::string kModuleName = "NacpProcess";
std::string mModuleName;
fnd::SharedPtr<fnd::IFile> mFile;
std::shared_ptr<tc::io::IStream> mFile;
CliOutputMode mCliOutputMode;
bool mVerify;
nn::hac::ApplicationControlProperty mNacp;
pie::hac::ApplicationControlProperty mNacp;
void importNacp();
void displayNacp();
};
};
}

File diff suppressed because it is too large Load diff

View file

@ -1,14 +1,13 @@
#pragma once
#include <string>
#include <fnd/types.h>
#include <fnd/IFile.h>
#include <fnd/SharedPtr.h>
#include <fnd/LayeredIntegrityMetadata.h>
#include <nn/hac/ContentArchiveHeader.h>
#include "KeyConfiguration.h"
#include "types.h"
#include "KeyBag.h"
#include "FsProcess.h"
#include <pietendo/hac/ContentArchiveHeader.h>
#include <pietendo/hac/HierarchicalIntegrityHeader.h>
#include <pietendo/hac/HierarchicalSha256Header.h>
#include "common.h"
namespace nstool {
class NcaProcess
{
@ -18,40 +17,40 @@ public:
void process();
// generic
void setInputFile(const fnd::SharedPtr<fnd::IFile>& file);
void setKeyCfg(const KeyConfiguration& keycfg);
void setInputFile(const std::shared_ptr<tc::io::IStream>& file);
void setKeyCfg(const KeyBag& keycfg);
void setCliOutputMode(CliOutputMode type);
void setVerifyMode(bool verify);
void setBaseNcaPath(const tc::Optional<tc::io::Path>& nca_path);
// nca specfic
void setPartition0ExtractPath(const std::string& path);
void setPartition1ExtractPath(const std::string& path);
void setPartition2ExtractPath(const std::string& path);
void setPartition3ExtractPath(const std::string& path);
void setListFs(bool list_fs);
// fs specific
void setShowFsTree(bool show_fs_tree);
void setFsRootLabel(const std::string& root_label);
void setExtractJobs(const std::vector<nstool::ExtractJob>& extract_jobs);
// post process() get FS out
const std::shared_ptr<tc::io::IFileSystem>& getFileSystem() const;
private:
const std::string kModuleName = "NcaProcess";
const std::string kNpdmExefsPath = "main.npdm";
const std::string kNpdmExefsPath = "/main.npdm";
std::string mModuleName;
// user options
fnd::SharedPtr<fnd::IFile> mFile;
KeyConfiguration mKeyCfg;
std::shared_ptr<tc::io::IStream> mFile;
KeyBag mKeyCfg;
CliOutputMode mCliOutputMode;
bool mVerify;
tc::Optional<tc::io::Path> mBaseNcaPath;
struct sExtract
{
std::string path;
bool doExtract;
} mPartitionPath[nn::hac::nca::kPartitionNum];
// fs processing
std::shared_ptr<tc::io::IFileSystem> mFileSystem;
FsProcess mFsProcess;
bool mListFs;
// data
nn::hac::sContentArchiveHeaderBlock mHdrBlock;
fnd::sha::sSha256Hash mHdrHash;
nn::hac::ContentArchiveHeader mHdr;
// nca data
pie::hac::sContentArchiveHeaderBlock mHdrBlock;
pie::hac::detail::sha256_hash_t mHdrHash;
pie::hac::ContentArchiveHeader mHdr;
// crypto
struct sKeys
@ -60,8 +59,8 @@ private:
{
byte_t index;
bool decrypted;
fnd::aes::sAes128Key enc;
fnd::aes::sAes128Key dec;
KeyBag::aes128_key_t enc;
KeyBag::aes128_key_t dec;
void operator=(const sKeyAreaKey& other)
{
@ -84,25 +83,46 @@ private:
return !(*this == other);
}
};
fnd::List<sKeyAreaKey> kak_list;
std::vector<sKeyAreaKey> kak_list;
sOptional<fnd::aes::sAes128Key> aes_ctr;
tc::Optional<pie::hac::detail::aes128_key_t> aes_ctr;
} mContentKey;
struct SparseInfo
{
};
// raw partition data
struct sPartitionInfo
{
fnd::SharedPtr<fnd::IFile> reader;
std::shared_ptr<tc::io::IStream> raw_reader; // raw unprocessed partition stream
std::shared_ptr<tc::io::IStream> decrypt_reader; // partition stream with transparent decryption
std::shared_ptr<tc::io::IStream> reader; // partition stream with transparent decryption & hash layer processing
tc::io::VirtualFileSystem::FileSystemSnapshot fs_snapshot;
std::shared_ptr<tc::io::IFileSystem> fs_reader;
std::string fail_reason;
size_t offset;
size_t size;
int64_t offset;
int64_t size;
// meta data
nn::hac::nca::FormatType format_type;
nn::hac::nca::HashType hash_type;
nn::hac::nca::EncryptionType enc_type;
fnd::LayeredIntegrityMetadata layered_intergrity_metadata;
fnd::aes::sAesIvCtr aes_ctr;
} mPartitions[nn::hac::nca::kPartitionNum];
pie::hac::nca::FormatType format_type;
pie::hac::nca::HashType hash_type;
pie::hac::nca::EncryptionType enc_type;
pie::hac::nca::MetaDataHashType metadata_hash_type;
// hash meta data
pie::hac::HierarchicalIntegrityHeader hierarchicalintegrity_hdr;
pie::hac::HierarchicalSha256Header hierarchicalsha256_hdr;
// crypto metadata
pie::hac::detail::aes_iv_t aes_ctr;
// sparse metadata
SparseInfo sparse_info;
};
std::array<sPartitionInfo, pie::hac::nca::kPartitionNum> mPartitions;
void importHeader();
void generateNcaBodyEncryptionKeys();
@ -111,5 +131,9 @@ private:
void displayHeader();
void processPartitions();
const char* getContentTypeForMountStr(nn::hac::nca::ContentType cont_type) const;
};
NcaProcess readBaseNCA();
std::string getContentTypeForMountStr(pie::hac::nca::ContentType cont_type) const;
};
}

View file

@ -1,25 +1,19 @@
#include <iostream>
#include <iomanip>
#include <fnd/SimpleTextOutput.h>
#include <fnd/OffsetAdjustedIFile.h>
#include <fnd/Vec.h>
#include <fnd/lz4.h>
#include <nn/hac/define/nro-hb.h>
#include "NroProcess.h"
NroProcess::NroProcess():
nstool::NroProcess::NroProcess() :
mModuleName("nstool::NroProcess"),
mFile(),
mCliOutputMode(_BIT(OUTPUT_BASIC)),
mCliOutputMode(true, false, false, false),
mVerify(false)
{
}
void NroProcess::process()
void nstool::NroProcess::process()
{
importHeader();
importCodeSegments();
if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
if (mCliOutputMode.show_basic_info)
displayHeader();
processRoMeta();
@ -28,86 +22,94 @@ void NroProcess::process()
mAssetProc.process();
}
void NroProcess::setInputFile(const fnd::SharedPtr<fnd::IFile>& file)
void nstool::NroProcess::setInputFile(const std::shared_ptr<tc::io::IStream>& file)
{
mFile = file;
}
void NroProcess::setCliOutputMode(CliOutputMode type)
void nstool::NroProcess::setCliOutputMode(CliOutputMode type)
{
mCliOutputMode = type;
}
void NroProcess::setVerifyMode(bool verify)
void nstool::NroProcess::setVerifyMode(bool verify)
{
mVerify = verify;
}
void NroProcess::setIs64BitInstruction(bool flag)
void nstool::NroProcess::setIs64BitInstruction(bool flag)
{
mRoMeta.setIs64BitInstruction(flag);
}
void NroProcess::setListApi(bool listApi)
void nstool::NroProcess::setListApi(bool listApi)
{
mRoMeta.setListApi(listApi);
}
void NroProcess::setListSymbols(bool listSymbols)
void nstool::NroProcess::setListSymbols(bool listSymbols)
{
mRoMeta.setListSymbols(listSymbols);
}
void NroProcess::setAssetListFs(bool list)
{
mAssetProc.setListFs(list);
}
void NroProcess::setAssetIconExtractPath(const std::string& path)
void nstool::NroProcess::setAssetIconExtractPath(const tc::io::Path& path)
{
mAssetProc.setIconExtractPath(path);
}
void NroProcess::setAssetNacpExtractPath(const std::string& path)
void nstool::NroProcess::setAssetNacpExtractPath(const tc::io::Path& path)
{
mAssetProc.setNacpExtractPath(path);
}
void NroProcess::setAssetRomfsExtractPath(const std::string& path)
void nstool::NroProcess::setAssetRomfsShowFsTree(bool show_fs_tree)
{
mAssetProc.setRomfsExtractPath(path);
mAssetProc.setRomfsShowFsTree(show_fs_tree);
}
const RoMetadataProcess& NroProcess::getRoMetadataProcess() const
void nstool::NroProcess::setAssetRomfsExtractJobs(const std::vector<nstool::ExtractJob>& extract_jobs)
{
mAssetProc.setRomfsExtractJobs(extract_jobs);
}
const nstool::RoMetadataProcess& nstool::NroProcess::getRoMetadataProcess() const
{
return mRoMeta;
}
void NroProcess::importHeader()
void nstool::NroProcess::importHeader()
{
fnd::Vec<byte_t> scratch;
if (*mFile == nullptr)
if (mFile == nullptr)
{
throw fnd::Exception(kModuleName, "No file reader set.");
throw tc::Exception(mModuleName, "No file reader set.");
}
if (mFile->canRead() == false || mFile->canSeek() == false)
{
throw tc::NotSupportedException(mModuleName, "Input stream requires read/seek permissions.");
}
if ((*mFile)->size() < sizeof(nn::hac::sNroHeader))
// check if file_size is smaller than NRO header size
if (tc::io::IOUtil::castInt64ToSize(mFile->length()) < sizeof(pie::hac::sNroHeader))
{
throw fnd::Exception(kModuleName, "Corrupt NRO: file too small");
throw tc::Exception(mModuleName, "Corrupt NRO: file too small.");
}
scratch.alloc(sizeof(nn::hac::sNroHeader));
(*mFile)->read(scratch.data(), 0, scratch.size());
// read nro
tc::ByteData scratch = tc::ByteData(sizeof(pie::hac::sNroHeader));
mFile->seek(0, tc::io::SeekOrigin::Begin);
mFile->read(scratch.data(), scratch.size());
// parse nro header
mHdr.fromBytes(scratch.data(), scratch.size());
// setup homebrew extension
nn::hac::sNroHeader* raw_hdr = (nn::hac::sNroHeader*)scratch.data();
if (((le_uint64_t*)raw_hdr->reserved_0)->get() == nn::hac::nro::kNroHomebrewStructMagic && (*mFile)->size() > mHdr.getNroSize())
pie::hac::sNroHeader* raw_hdr = (pie::hac::sNroHeader*)scratch.data();
int64_t file_size = mFile->length();
if (((tc::bn::le64<uint64_t>*)raw_hdr->reserved_0.data())->unwrap() == pie::hac::nro::kNroHomebrewStructMagic && file_size > int64_t(mHdr.getNroSize()))
{
mIsHomebrewNro = true;
mAssetProc.setInputFile(new fnd::OffsetAdjustedIFile(mFile, mHdr.getNroSize(), (*mFile)->size() - mHdr.getNroSize()));
mAssetProc.setInputFile(std::make_shared<tc::io::SubStream>(tc::io::SubStream(mFile, int64_t(mHdr.getNroSize()), file_size - int64_t(mHdr.getNroSize()))));
mAssetProc.setCliOutputMode(mCliOutputMode);
mAssetProc.setVerifyMode(mVerify);
}
@ -115,51 +117,65 @@ void NroProcess::importHeader()
mIsHomebrewNro = false;
}
void NroProcess::importCodeSegments()
void nstool::NroProcess::importCodeSegments()
{
mTextBlob.alloc(mHdr.getTextInfo().size);
(*mFile)->read(mTextBlob.data(), mHdr.getTextInfo().memory_offset, mTextBlob.size());
mRoBlob.alloc(mHdr.getRoInfo().size);
(*mFile)->read(mRoBlob.data(), mHdr.getRoInfo().memory_offset, mRoBlob.size());
mDataBlob.alloc(mHdr.getDataInfo().size);
(*mFile)->read(mDataBlob.data(), mHdr.getDataInfo().memory_offset, mDataBlob.size());
}
void NroProcess::displayHeader()
{
std::cout << "[NRO Header]" << std::endl;
std::cout << " RoCrt: " << std::endl;
std::cout << " EntryPoint: 0x" << std::hex << mHdr.getRoCrtEntryPoint() << std::endl;
std::cout << " ModOffset: 0x" << std::hex << mHdr.getRoCrtModOffset() << std::endl;
std::cout << " ModuleId: " << fnd::SimpleTextOutput::arrayToString(mHdr.getModuleId().data, nn::hac::nro::kModuleIdSize, false, "") << std::endl;
std::cout << " NroSize: 0x" << std::hex << mHdr.getNroSize() << std::endl;
std::cout << " Program Sections:" << std::endl;
std::cout << " .text:" << std::endl;
std::cout << " Offset: 0x" << std::hex << mHdr.getTextInfo().memory_offset << std::endl;
std::cout << " Size: 0x" << std::hex << mHdr.getTextInfo().size << std::endl;
std::cout << " .ro:" << std::endl;
std::cout << " Offset: 0x" << std::hex << mHdr.getRoInfo().memory_offset << std::endl;
std::cout << " Size: 0x" << std::hex << mHdr.getRoInfo().size << std::endl;
if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mHdr.getTextInfo().size > 0)
{
std::cout << " .api_info:" << std::endl;
std::cout << " Offset: 0x" << std::hex << mHdr.getRoEmbeddedInfo().memory_offset << std::endl;
std::cout << " Size: 0x" << std::hex << mHdr.getRoEmbeddedInfo().size << std::endl;
std::cout << " .dynstr:" << std::endl;
std::cout << " Offset: 0x" << std::hex << mHdr.getRoDynStrInfo().memory_offset << std::endl;
std::cout << " Size: 0x" << std::hex << mHdr.getRoDynStrInfo().size << std::endl;
std::cout << " .dynsym:" << std::endl;
std::cout << " Offset: 0x" << std::hex << mHdr.getRoDynSymInfo().memory_offset << std::endl;
std::cout << " Size: 0x" << std::hex << mHdr.getRoDynSymInfo().size << std::endl;
mTextBlob = tc::ByteData(mHdr.getTextInfo().size);
mFile->seek(mHdr.getTextInfo().memory_offset, tc::io::SeekOrigin::Begin);
mFile->read(mTextBlob.data(), mTextBlob.size());
}
if (mHdr.getRoInfo().size > 0)
{
mRoBlob = tc::ByteData(mHdr.getRoInfo().size);
mFile->seek(mHdr.getRoInfo().memory_offset, tc::io::SeekOrigin::Begin);
mFile->read(mRoBlob.data(), mRoBlob.size());
}
if (mHdr.getDataInfo().size > 0)
{
mDataBlob = tc::ByteData(mHdr.getDataInfo().size);
mFile->seek(mHdr.getDataInfo().memory_offset, tc::io::SeekOrigin::Begin);
mFile->read(mDataBlob.data(), mDataBlob.size());
}
std::cout << " .data:" << std::endl;
std::cout << " Offset: 0x" << std::hex << mHdr.getDataInfo().memory_offset << std::endl;
std::cout << " Size: 0x" << std::hex << mHdr.getDataInfo().size << std::endl;
std::cout << " .bss:" << std::endl;
std::cout << " Size: 0x" << std::hex << mHdr.getBssSize() << std::endl;
}
void NroProcess::processRoMeta()
void nstool::NroProcess::displayHeader()
{
fmt::print("[NRO Header]\n");
fmt::print(" RoCrt: \n");
fmt::print(" EntryPoint: 0x{:x}\n", mHdr.getRoCrtEntryPoint());
fmt::print(" ModOffset: 0x{:x}\n", mHdr.getRoCrtModOffset());
fmt::print(" ModuleId: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(mHdr.getModuleId().data(), mHdr.getModuleId().size(), false, ""));
fmt::print(" NroSize: 0x{:x}\n", mHdr.getNroSize());
fmt::print(" Program Sections:\n");
fmt::print(" .text:\n");
fmt::print(" Offset: 0x{:x}\n", mHdr.getTextInfo().memory_offset);
fmt::print(" Size: 0x{:x}\n", mHdr.getTextInfo().size);
fmt::print(" .ro:\n");
fmt::print(" Offset: 0x{:x}\n", mHdr.getRoInfo().memory_offset);
fmt::print(" Size: 0x{:x}\n", mHdr.getRoInfo().size);
if (mCliOutputMode.show_extended_info)
{
fmt::print(" .api_info:\n");
fmt::print(" Offset: 0x{:x}\n", mHdr.getRoEmbeddedInfo().memory_offset);
fmt::print(" Size: 0x{:x}\n", mHdr.getRoEmbeddedInfo().size);
fmt::print(" .dynstr:\n");
fmt::print(" Offset: 0x{:x}\n", mHdr.getRoDynStrInfo().memory_offset);
fmt::print(" Size: 0x{:x}\n", mHdr.getRoDynStrInfo().size);
fmt::print(" .dynsym:\n");
fmt::print(" Offset: 0x{:x}\n", mHdr.getRoDynSymInfo().memory_offset);
fmt::print(" Size: 0x{:x}\n", mHdr.getRoDynSymInfo().size);
}
fmt::print(" .data:\n");
fmt::print(" Offset: 0x{:x}\n", mHdr.getDataInfo().memory_offset);
fmt::print(" Size: 0x{:x}\n", mHdr.getDataInfo().size);
fmt::print(" .bss:\n");
fmt::print(" Size: 0x{:x}\n", mHdr.getBssSize());
}
void nstool::NroProcess::processRoMeta()
{
if (mRoBlob.size())
{

View file

@ -1,15 +1,11 @@
#pragma once
#include <vector>
#include <string>
#include <fnd/types.h>
#include <fnd/IFile.h>
#include <fnd/SharedPtr.h>
#include <nn/hac/define/meta.h>
#include <nn/hac/NroHeader.h>
#include "types.h"
#include "RoMetadataProcess.h"
#include "AssetProcess.h"
#include "common.h"
#include "RoMetadataProcess.h"
#include <pietendo/hac/NroHeader.h>
namespace nstool {
class NroProcess
{
@ -18,7 +14,7 @@ public:
void process();
void setInputFile(const fnd::SharedPtr<fnd::IFile>& file);
void setInputFile(const std::shared_ptr<tc::io::IStream>& file);
void setCliOutputMode(CliOutputMode type);
void setVerifyMode(bool verify);
@ -27,27 +23,29 @@ public:
void setListSymbols(bool listSymbols);
// for homebrew NROs with Asset blobs appended
void setAssetListFs(bool list);
void setAssetIconExtractPath(const std::string& path);
void setAssetNacpExtractPath(const std::string& path);
void setAssetRomfsExtractPath(const std::string& path);
void setAssetIconExtractPath(const tc::io::Path& path);
void setAssetNacpExtractPath(const tc::io::Path& path);
void setAssetRomfsShowFsTree(bool show_fs_tree);
void setAssetRomfsExtractJobs(const std::vector<nstool::ExtractJob>& extract_jobs);
const RoMetadataProcess& getRoMetadataProcess() const;
const nstool::RoMetadataProcess& getRoMetadataProcess() const;
private:
const std::string kModuleName = "NroProcess";
std::string mModuleName;
fnd::SharedPtr<fnd::IFile> mFile;
std::shared_ptr<tc::io::IStream> mFile;
CliOutputMode mCliOutputMode;
bool mVerify;
nn::hac::NroHeader mHdr;
fnd::Vec<byte_t> mTextBlob, mRoBlob, mDataBlob;
RoMetadataProcess mRoMeta;
pie::hac::NroHeader mHdr;
tc::ByteData mTextBlob, mRoBlob, mDataBlob;
nstool::RoMetadataProcess mRoMeta;
bool mIsHomebrewNro;
AssetProcess mAssetProc;
nstool::AssetProcess mAssetProc;
void importHeader();
void importCodeSegments();
void displayHeader();
void processRoMeta();
};
};
}

View file

@ -1,14 +1,11 @@
#include <iostream>
#include <iomanip>
#include <fnd/SimpleTextOutput.h>
#include <fnd/OffsetAdjustedIFile.h>
#include <fnd/Vec.h>
#include <fnd/lz4.h>
#include "NsoProcess.h"
NsoProcess::NsoProcess():
#include <lz4.h>
nstool::NsoProcess::NsoProcess() :
mModuleName("nstool::NsoProcess"),
mFile(),
mCliOutputMode(_BIT(OUTPUT_BASIC)),
mCliOutputMode(true, false, false, false),
mVerify(false),
mIs64BitInstruction(true),
mListApi(false),
@ -16,216 +13,243 @@ NsoProcess::NsoProcess():
{
}
void NsoProcess::process()
void nstool::NsoProcess::process()
{
importHeader();
importCodeSegments();
if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
if (mCliOutputMode.show_basic_info)
displayNsoHeader();
processRoMeta();
}
void NsoProcess::setInputFile(const fnd::SharedPtr<fnd::IFile>& file)
void nstool::NsoProcess::setInputFile(const std::shared_ptr<tc::io::IStream>& file)
{
mFile = file;
}
void NsoProcess::setCliOutputMode(CliOutputMode type)
void nstool::NsoProcess::setCliOutputMode(CliOutputMode type)
{
mCliOutputMode = type;
}
void NsoProcess::setVerifyMode(bool verify)
void nstool::NsoProcess::setVerifyMode(bool verify)
{
mVerify = verify;
}
void NsoProcess::setIs64BitInstruction(bool flag)
void nstool::NsoProcess::setIs64BitInstruction(bool flag)
{
mRoMeta.setIs64BitInstruction(flag);
}
void NsoProcess::setListApi(bool listApi)
void nstool::NsoProcess::setListApi(bool listApi)
{
mRoMeta.setListApi(listApi);
}
void NsoProcess::setListSymbols(bool listSymbols)
void nstool::NsoProcess::setListSymbols(bool listSymbols)
{
mRoMeta.setListSymbols(listSymbols);
}
const RoMetadataProcess& NsoProcess::getRoMetadataProcess() const
const nstool::RoMetadataProcess& nstool::NsoProcess::getRoMetadataProcess() const
{
return mRoMeta;
}
void NsoProcess::importHeader()
void nstool::NsoProcess::importHeader()
{
fnd::Vec<byte_t> scratch;
if (*mFile == nullptr)
if (mFile == nullptr)
{
throw fnd::Exception(kModuleName, "No file reader set.");
throw tc::Exception(mModuleName, "No file reader set.");
}
if (mFile->canRead() == false || mFile->canSeek() == false)
{
throw tc::NotSupportedException(mModuleName, "Input stream requires read/seek permissions.");
}
if ((*mFile)->size() < sizeof(nn::hac::sNsoHeader))
// check if file_size is smaller than NSO header size
size_t file_size = tc::io::IOUtil::castInt64ToSize(mFile->length());
if (file_size < sizeof(pie::hac::sNsoHeader))
{
throw fnd::Exception(kModuleName, "Corrupt NSO: file too small");
throw tc::Exception(mModuleName, "Corrupt NSO: file too small.");
}
scratch.alloc(sizeof(nn::hac::sNsoHeader));
(*mFile)->read(scratch.data(), 0, scratch.size());
// read nso
tc::ByteData scratch = tc::ByteData(sizeof(pie::hac::sNsoHeader));
mFile->seek(0, tc::io::SeekOrigin::Begin);
mFile->read(scratch.data(), scratch.size());
// parse nso header
mHdr.fromBytes(scratch.data(), scratch.size());
}
void NsoProcess::importCodeSegments()
void nstool::NsoProcess::importCodeSegments()
{
fnd::Vec<byte_t> scratch;
uint32_t decompressed_len;
fnd::sha::sSha256Hash calc_hash;
tc::ByteData scratch;
pie::hac::detail::sha256_hash_t calc_hash;
// process text segment
if (mHdr.getTextSegmentInfo().is_compressed)
{
scratch.alloc(mHdr.getTextSegmentInfo().file_layout.size);
(*mFile)->read(scratch.data(), mHdr.getTextSegmentInfo().file_layout.offset, scratch.size());
mTextBlob.alloc(mHdr.getTextSegmentInfo().memory_layout.size);
fnd::lz4::decompressData(scratch.data(), (uint32_t)scratch.size(), mTextBlob.data(), (uint32_t)mTextBlob.size(), decompressed_len);
if (decompressed_len != mTextBlob.size())
// allocate/read compressed text
scratch = tc::ByteData(mHdr.getTextSegmentInfo().file_layout.size);
mFile->seek(mHdr.getTextSegmentInfo().file_layout.offset, tc::io::SeekOrigin::Begin);
mFile->read(scratch.data(), scratch.size());
// allocate for decompressed text segment
mTextBlob = tc::ByteData(mHdr.getTextSegmentInfo().memory_layout.size);
// decompress text segment
if (decompressData(scratch.data(), scratch.size(), mTextBlob.data(), mTextBlob.size()) != mTextBlob.size())
{
throw fnd::Exception(kModuleName, "NSO text segment failed to decompress");
throw tc::Exception(mModuleName, "NSO text segment failed to decompress");
}
}
else
{
mTextBlob.alloc(mHdr.getTextSegmentInfo().file_layout.size);
(*mFile)->read(mTextBlob.data(), mHdr.getTextSegmentInfo().file_layout.offset, mTextBlob.size());
// read text segment directly (not compressed)
mTextBlob = tc::ByteData(mHdr.getTextSegmentInfo().file_layout.size);
mFile->seek(mHdr.getTextSegmentInfo().file_layout.offset, tc::io::SeekOrigin::Begin);
mFile->read(mTextBlob.data(), mTextBlob.size());
}
if (mHdr.getTextSegmentInfo().is_hashed)
{
fnd::sha::Sha256(mTextBlob.data(), mTextBlob.size(), calc_hash.bytes);
tc::crypto::GenerateSha2256Hash(calc_hash.data(), mTextBlob.data(), mTextBlob.size());
if (calc_hash != mHdr.getTextSegmentInfo().hash)
{
throw fnd::Exception(kModuleName, "NSO text segment failed SHA256 verification");
throw tc::Exception(mModuleName, "NSO text segment failed SHA256 verification");
}
}
// process ro segment
if (mHdr.getRoSegmentInfo().is_compressed)
{
scratch.alloc(mHdr.getRoSegmentInfo().file_layout.size);
(*mFile)->read(scratch.data(), mHdr.getRoSegmentInfo().file_layout.offset, scratch.size());
mRoBlob.alloc(mHdr.getRoSegmentInfo().memory_layout.size);
fnd::lz4::decompressData(scratch.data(), (uint32_t)scratch.size(), mRoBlob.data(), (uint32_t)mRoBlob.size(), decompressed_len);
if (decompressed_len != mRoBlob.size())
// allocate/read compressed ro segment
scratch = tc::ByteData(mHdr.getRoSegmentInfo().file_layout.size);
mFile->seek(mHdr.getRoSegmentInfo().file_layout.offset, tc::io::SeekOrigin::Begin);
mFile->read(scratch.data(), scratch.size());
// allocate for decompressed ro segment
mRoBlob = tc::ByteData(mHdr.getRoSegmentInfo().memory_layout.size);
// decompress ro segment
if (decompressData(scratch.data(), scratch.size(), mRoBlob.data(), mRoBlob.size()) != mRoBlob.size())
{
throw fnd::Exception(kModuleName, "NSO ro segment failed to decompress");
throw tc::Exception(mModuleName, "NSO ro segment failed to decompress");
}
}
else
{
mRoBlob.alloc(mHdr.getRoSegmentInfo().file_layout.size);
(*mFile)->read(mRoBlob.data(), mHdr.getRoSegmentInfo().file_layout.offset, mRoBlob.size());
// read ro segment directly (not compressed)
mRoBlob = tc::ByteData(mHdr.getRoSegmentInfo().file_layout.size);
mFile->seek(mHdr.getRoSegmentInfo().file_layout.offset, tc::io::SeekOrigin::Begin);
mFile->read(mRoBlob.data(), mRoBlob.size());
}
if (mHdr.getRoSegmentInfo().is_hashed)
{
fnd::sha::Sha256(mRoBlob.data(), mRoBlob.size(), calc_hash.bytes);
tc::crypto::GenerateSha2256Hash(calc_hash.data(), mRoBlob.data(), mRoBlob.size());
if (calc_hash != mHdr.getRoSegmentInfo().hash)
{
throw fnd::Exception(kModuleName, "NSO ro segment failed SHA256 verification");
throw tc::Exception(mModuleName, "NSO ro segment failed SHA256 verification");
}
}
// process data segment
// process ro segment
if (mHdr.getDataSegmentInfo().is_compressed)
{
scratch.alloc(mHdr.getDataSegmentInfo().file_layout.size);
(*mFile)->read(scratch.data(), mHdr.getDataSegmentInfo().file_layout.offset, scratch.size());
mDataBlob.alloc(mHdr.getDataSegmentInfo().memory_layout.size);
fnd::lz4::decompressData(scratch.data(), (uint32_t)scratch.size(), mDataBlob.data(), (uint32_t)mDataBlob.size(), decompressed_len);
if (decompressed_len != mDataBlob.size())
// allocate/read compressed ro segment
scratch = tc::ByteData(mHdr.getDataSegmentInfo().file_layout.size);
mFile->seek(mHdr.getDataSegmentInfo().file_layout.offset, tc::io::SeekOrigin::Begin);
mFile->read(scratch.data(), scratch.size());
// allocate for decompressed ro segment
mDataBlob = tc::ByteData(mHdr.getDataSegmentInfo().memory_layout.size);
// decompress ro segment
if (decompressData(scratch.data(), scratch.size(), mDataBlob.data(), mDataBlob.size()) != mDataBlob.size())
{
throw fnd::Exception(kModuleName, "NSO data segment failed to decompress");
throw tc::Exception(mModuleName, "NSO data segment failed to decompress");
}
}
else
{
mDataBlob.alloc(mHdr.getDataSegmentInfo().file_layout.size);
(*mFile)->read(mDataBlob.data(), mHdr.getDataSegmentInfo().file_layout.offset, mDataBlob.size());
// read ro segment directly (not compressed)
mDataBlob = tc::ByteData(mHdr.getDataSegmentInfo().file_layout.size);
mFile->seek(mHdr.getDataSegmentInfo().file_layout.offset, tc::io::SeekOrigin::Begin);
mFile->read(mDataBlob.data(), mDataBlob.size());
}
if (mHdr.getDataSegmentInfo().is_hashed)
{
fnd::sha::Sha256(mDataBlob.data(), mDataBlob.size(), calc_hash.bytes);
tc::crypto::GenerateSha2256Hash(calc_hash.data(), mDataBlob.data(), mDataBlob.size());
if (calc_hash != mHdr.getDataSegmentInfo().hash)
{
throw fnd::Exception(kModuleName, "NSO data segment failed SHA256 verification");
throw tc::Exception(mModuleName, "NSO data segment failed SHA256 verification");
}
}
}
void NsoProcess::displayNsoHeader()
void nstool::NsoProcess::displayNsoHeader()
{
std::cout << "[NSO Header]" << std::endl;
std::cout << " ModuleId: " << fnd::SimpleTextOutput::arrayToString(mHdr.getModuleId().data, nn::hac::nso::kModuleIdSize, false, "") << std::endl;
if (_HAS_BIT(mCliOutputMode, OUTPUT_LAYOUT))
fmt::print("[NSO Header]\n");
fmt::print(" ModuleId: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(mHdr.getModuleId().data(), mHdr.getModuleId().size(), false, ""));
if (mCliOutputMode.show_layout)
{
std::cout << " Program Segments:" << std::endl;
std::cout << " .module_name:" << std::endl;
std::cout << " FileOffset: 0x" << std::hex << mHdr.getModuleNameInfo().offset << std::endl;
std::cout << " FileSize: 0x" << std::hex << mHdr.getModuleNameInfo().size << std::endl;
std::cout << " .text:" << std::endl;
std::cout << " FileOffset: 0x" << std::hex << mHdr.getTextSegmentInfo().file_layout.offset << std::endl;
std::cout << " FileSize: 0x" << std::hex << mHdr.getTextSegmentInfo().file_layout.size << (mHdr.getTextSegmentInfo().is_compressed? " (COMPRESSED)" : "") << std::endl;
std::cout << " .ro:" << std::endl;
std::cout << " FileOffset: 0x" << std::hex << mHdr.getRoSegmentInfo().file_layout.offset << std::endl;
std::cout << " FileSize: 0x" << std::hex << mHdr.getRoSegmentInfo().file_layout.size << (mHdr.getRoSegmentInfo().is_compressed? " (COMPRESSED)" : "") << std::endl;
std::cout << " .data:" << std::endl;
std::cout << " FileOffset: 0x" << std::hex << mHdr.getDataSegmentInfo().file_layout.offset << std::endl;
std::cout << " FileSize: 0x" << std::hex << mHdr.getDataSegmentInfo().file_layout.size << (mHdr.getDataSegmentInfo().is_compressed? " (COMPRESSED)" : "") << std::endl;
fmt::print(" Program Segments:\n");
fmt::print(" .module_name:\n");
fmt::print(" FileOffset: 0x{:x}\n", mHdr.getModuleNameInfo().offset);
fmt::print(" FileSize: 0x{:x}\n", mHdr.getModuleNameInfo().size);
fmt::print(" .text:\n");
fmt::print(" FileOffset: 0x{:x}\n", mHdr.getTextSegmentInfo().file_layout.offset);
fmt::print(" FileSize: 0x{:x}{:s}\n", mHdr.getTextSegmentInfo().file_layout.size, (mHdr.getTextSegmentInfo().is_compressed? " (COMPRESSED)" : ""));
fmt::print(" .ro:\n");
fmt::print(" FileOffset: 0x{:x}\n", mHdr.getRoSegmentInfo().file_layout.offset);
fmt::print(" FileSize: 0x{:x}{:s}\n", mHdr.getRoSegmentInfo().file_layout.size, (mHdr.getRoSegmentInfo().is_compressed? " (COMPRESSED)" : ""));
fmt::print(" .data:\n");
fmt::print(" FileOffset: 0x{:x}\n", mHdr.getDataSegmentInfo().file_layout.offset);
fmt::print(" FileSize: 0x{:x}{:s}\n", mHdr.getDataSegmentInfo().file_layout.size, (mHdr.getDataSegmentInfo().is_compressed? " (COMPRESSED)" : ""));
}
std::cout << " Program Sections:" << std::endl;
std::cout << " .text:" << std::endl;
std::cout << " MemoryOffset: 0x" << std::hex << mHdr.getTextSegmentInfo().memory_layout.offset << std::endl;
std::cout << " MemorySize: 0x" << std::hex << mHdr.getTextSegmentInfo().memory_layout.size << std::endl;
if (mHdr.getTextSegmentInfo().is_hashed && _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
fmt::print(" Program Sections:\n");
fmt::print(" .text:\n");
fmt::print(" MemoryOffset: 0x{:x}\n", mHdr.getTextSegmentInfo().memory_layout.offset);
fmt::print(" MemorySize: 0x{:x}\n", mHdr.getTextSegmentInfo().memory_layout.size);
if (mHdr.getTextSegmentInfo().is_hashed && mCliOutputMode.show_extended_info)
{
std::cout << " Hash: " << fnd::SimpleTextOutput::arrayToString(mHdr.getTextSegmentInfo().hash.bytes, 32, false, "") << std::endl;
fmt::print(" Hash: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(mHdr.getTextSegmentInfo().hash.data(), mHdr.getTextSegmentInfo().hash.size(), false, ""));
}
std::cout << " .ro:" << std::endl;
std::cout << " MemoryOffset: 0x" << std::hex << mHdr.getRoSegmentInfo().memory_layout.offset << std::endl;
std::cout << " MemorySize: 0x" << std::hex << mHdr.getRoSegmentInfo().memory_layout.size << std::endl;
if (mHdr.getRoSegmentInfo().is_hashed && _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
fmt::print(" .ro:\n");
fmt::print(" MemoryOffset: 0x{:x}\n", mHdr.getRoSegmentInfo().memory_layout.offset);
fmt::print(" MemorySize: 0x{:x}\n", mHdr.getRoSegmentInfo().memory_layout.size);
if (mHdr.getRoSegmentInfo().is_hashed && mCliOutputMode.show_extended_info)
{
std::cout << " Hash: " << fnd::SimpleTextOutput::arrayToString(mHdr.getRoSegmentInfo().hash.bytes, 32, false, "") << std::endl;
fmt::print(" Hash: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(mHdr.getRoSegmentInfo().hash.data(), mHdr.getRoSegmentInfo().hash.size(), false, ""));
}
if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
if (mCliOutputMode.show_extended_info)
{
std::cout << " .api_info:" << std::endl;
std::cout << " MemoryOffset: 0x" << std::hex << mHdr.getRoEmbeddedInfo().offset << std::endl;
std::cout << " MemorySize: 0x" << std::hex << mHdr.getRoEmbeddedInfo().size << std::endl;
std::cout << " .dynstr:" << std::endl;
std::cout << " MemoryOffset: 0x" << std::hex << mHdr.getRoDynStrInfo().offset << std::endl;
std::cout << " MemorySize: 0x" << std::hex << mHdr.getRoDynStrInfo().size << std::endl;
std::cout << " .dynsym:" << std::endl;
std::cout << " MemoryOffset: 0x" << std::hex << mHdr.getRoDynSymInfo().offset << std::endl;
std::cout << " MemorySize: 0x" << std::hex << mHdr.getRoDynSymInfo().size << std::endl;
fmt::print(" .api_info:\n");
fmt::print(" MemoryOffset: 0x{:x}\n", mHdr.getRoEmbeddedInfo().offset);
fmt::print(" MemorySize: 0x{:x}\n", mHdr.getRoEmbeddedInfo().size);
fmt::print(" .dynstr:\n");
fmt::print(" MemoryOffset: 0x{:x}\n", mHdr.getRoDynStrInfo().offset);
fmt::print(" MemorySize: 0x{:x}\n", mHdr.getRoDynStrInfo().size);
fmt::print(" .dynsym:\n");
fmt::print(" MemoryOffset: 0x{:x}\n", mHdr.getRoDynSymInfo().offset);
fmt::print(" MemorySize: 0x{:x}\n", mHdr.getRoDynSymInfo().size);
}
std::cout << " .data:" << std::endl;
std::cout << " MemoryOffset: 0x" << std::hex << mHdr.getDataSegmentInfo().memory_layout.offset << std::endl;
std::cout << " MemorySize: 0x" << std::hex << mHdr.getDataSegmentInfo().memory_layout.size << std::endl;
if (mHdr.getDataSegmentInfo().is_hashed && _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
fmt::print(" .data:\n");
fmt::print(" MemoryOffset: 0x{:x}\n", mHdr.getDataSegmentInfo().memory_layout.offset);
fmt::print(" MemorySize: 0x{:x}\n", mHdr.getDataSegmentInfo().memory_layout.size);
if (mHdr.getDataSegmentInfo().is_hashed && mCliOutputMode.show_extended_info)
{
std::cout << " Hash: " << fnd::SimpleTextOutput::arrayToString(mHdr.getDataSegmentInfo().hash.bytes, 32, false, "") << std::endl;
fmt::print(" Hash: {:s}\n", tc::cli::FormatUtil::formatBytesAsString(mHdr.getDataSegmentInfo().hash.data(), mHdr.getDataSegmentInfo().hash.size(), false, ""));
}
std::cout << " .bss:" << std::endl;
std::cout << " MemorySize: 0x" << std::hex << mHdr.getBssSize() << std::endl;
fmt::print(" .bss:\n");
fmt::print(" MemorySize: 0x{:x}\n", mHdr.getBssSize());
}
void NsoProcess::processRoMeta()
void nstool::NsoProcess::processRoMeta()
{
if (mRoBlob.size())
{
@ -237,4 +261,25 @@ void NsoProcess::processRoMeta()
mRoMeta.setCliOutputMode(mCliOutputMode);
mRoMeta.process();
}
}
size_t nstool::NsoProcess::decompressData(const byte_t* src, size_t src_len, byte_t* dst, size_t dst_capacity)
{
if (src_len >= LZ4_MAX_INPUT_SIZE)
{
return 0;
}
int32_t src_len_input = int32_t(src_len);
int32_t dst_capcacity_input = (dst_capacity < LZ4_MAX_INPUT_SIZE) ? int32_t(dst_capacity) : LZ4_MAX_INPUT_SIZE;
int32_t decomp_size = LZ4_decompress_safe((const char*)src, (char*)dst, src_len_input, dst_capcacity_input);
if (decomp_size < 0)
{
memset(dst, 0, dst_capacity);
return 0;
}
return size_t(decomp_size);
}

View file

@ -1,15 +1,12 @@
#pragma once
#include <vector>
#include <string>
#include <fnd/types.h>
#include <fnd/IFile.h>
#include <fnd/SharedPtr.h>
#include <nn/hac/define/meta.h>
#include <nn/hac/NsoHeader.h>
#include "common.h"
#include "types.h"
#include "RoMetadataProcess.h"
#include <pietendo/hac/define/meta.h>
#include <pietendo/hac/NsoHeader.h>
namespace nstool {
class NsoProcess
{
public:
@ -17,7 +14,7 @@ public:
void process();
void setInputFile(const fnd::SharedPtr<fnd::IFile>& file);
void setInputFile(const std::shared_ptr<tc::io::IStream>& file);
void setCliOutputMode(CliOutputMode type);
void setVerifyMode(bool verify);
@ -25,23 +22,27 @@ public:
void setListApi(bool listApi);
void setListSymbols(bool listSymbols);
const RoMetadataProcess& getRoMetadataProcess() const;
const nstool::RoMetadataProcess& getRoMetadataProcess() const;
private:
const std::string kModuleName = "NsoProcess";
std::string mModuleName;
fnd::SharedPtr<fnd::IFile> mFile;
std::shared_ptr<tc::io::IStream> mFile;
CliOutputMode mCliOutputMode;
bool mVerify;
bool mIs64BitInstruction;
bool mListApi;
bool mListSymbols;
nn::hac::NsoHeader mHdr;
fnd::Vec<byte_t> mTextBlob, mRoBlob, mDataBlob;
RoMetadataProcess mRoMeta;
pie::hac::NsoHeader mHdr;
tc::ByteData mTextBlob, mRoBlob, mDataBlob;
nstool::RoMetadataProcess mRoMeta;
void importHeader();
void importCodeSegments();
void displayNsoHeader();
void processRoMeta();
};
size_t decompressData(const byte_t* src, size_t src_len, byte_t* dst, size_t dst_capacity);
};
}

View file

@ -1,199 +1,132 @@
#include "PfsProcess.h"
#include "util.h"
#include <iostream>
#include <iomanip>
#include <pietendo/hac/PartitionFsUtil.h>
#include <tc/io/LocalFileSystem.h>
#include <fnd/SimpleFile.h>
#include <fnd/io.h>
#include <nn/hac/PartitionFsUtil.h>
#include <tc/io/VirtualFileSystem.h>
#include <pietendo/hac/PartitionFsSnapshotGenerator.h>
PfsProcess::PfsProcess() :
nstool::PfsProcess::PfsProcess() :
mModuleName("nstool::PfsProcess"),
mFile(),
mCliOutputMode(_BIT(OUTPUT_BASIC)),
mCliOutputMode(true, false, false, false),
mVerify(false),
mExtractPath(),
mExtract(false),
mMountName(),
mListFs(false),
mPfs()
mPfs(),
mFileSystem(),
mFsProcess()
{
mFsProcess.setFsFormatName("PartitionFs");
}
void PfsProcess::process()
void nstool::PfsProcess::process()
{
importHeader();
if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
if (mFile == nullptr)
{
displayHeader();
if (mListFs || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
displayFs();
throw tc::Exception(mModuleName, "No file reader set.");
}
if (mPfs.getFsType() == mPfs.TYPE_HFS0 && mVerify)
validateHfs();
if (mExtract)
extractFs();
if (mFile->canRead() == false || mFile->canSeek() == false)
{
throw tc::NotSupportedException(mModuleName, "Input stream requires read/seek permissions.");
}
tc::ByteData scratch;
// read base header to determine complete header size
if (mFile->length() < tc::io::IOUtil::castSizeToInt64(sizeof(pie::hac::sPfsHeader)))
{
throw tc::Exception(mModuleName, "Corrupt PartitionFs: File too small");
}
scratch = tc::ByteData(sizeof(pie::hac::sPfsHeader));
mFile->seek(0, tc::io::SeekOrigin::Begin);
mFile->read(scratch.data(), scratch.size());
if (validateHeaderMagic(((pie::hac::sPfsHeader*)scratch.data())) == false)
{
throw tc::Exception(mModuleName, "Corrupt PartitionFs: Header had incorrect struct magic.");
}
// read complete size header
size_t pfsHeaderSize = determineHeaderSize(((pie::hac::sPfsHeader*)scratch.data()));
if (mFile->length() < tc::io::IOUtil::castSizeToInt64(pfsHeaderSize))
{
throw tc::Exception(mModuleName, "Corrupt PartitionFs: File too small");
}
scratch = tc::ByteData(pfsHeaderSize);
mFile->seek(0, tc::io::SeekOrigin::Begin);
mFile->read(scratch.data(), scratch.size());
// process PFS
mPfs.fromBytes(scratch.data(), scratch.size());
// create virtual filesystem
mFileSystem = std::make_shared<tc::io::VirtualFileSystem>(tc::io::VirtualFileSystem(pie::hac::PartitionFsSnapshotGenerator(mFile, mVerify ? pie::hac::PartitionFsSnapshotGenerator::ValidationMode_Warn : pie::hac::PartitionFsSnapshotGenerator::ValidationMode_None)));
mFsProcess.setInputFileSystem(mFileSystem);
// set properties for FsProcess
mFsProcess.setFsProperties({
fmt::format("Type: {:s}", pie::hac::PartitionFsUtil::getFsTypeAsString(mPfs.getFsType())),
fmt::format("FileNum: {:d}", mPfs.getFileList().size())
});
mFsProcess.process();
}
void PfsProcess::setInputFile(const fnd::SharedPtr<fnd::IFile>& file)
void nstool::PfsProcess::setInputFile(const std::shared_ptr<tc::io::IStream>& file)
{
mFile = file;
}
void PfsProcess::setCliOutputMode(CliOutputMode type)
void nstool::PfsProcess::setCliOutputMode(CliOutputMode type)
{
mCliOutputMode = type;
mFsProcess.setShowFsInfo(mCliOutputMode.show_basic_info);
}
void PfsProcess::setVerifyMode(bool verify)
void nstool::PfsProcess::setVerifyMode(bool verify)
{
mVerify = verify;
}
void PfsProcess::setMountPointName(const std::string& mount_name)
void nstool::PfsProcess::setShowFsTree(bool show_fs_tree)
{
mMountName = mount_name;
mFsProcess.setShowFsTree(show_fs_tree);
}
void PfsProcess::setExtractPath(const std::string& path)
void nstool::PfsProcess::setFsRootLabel(const std::string& root_label)
{
mExtract = true;
mExtractPath = path;
mFsProcess.setFsRootLabel(root_label);
}
void PfsProcess::setListFs(bool list_fs)
void nstool::PfsProcess::setExtractJobs(const std::vector<nstool::ExtractJob>& extract_jobs)
{
mListFs = list_fs;
mFsProcess.setExtractJobs(extract_jobs);
}
const nn::hac::PartitionFsHeader& PfsProcess::getPfsHeader() const
const pie::hac::PartitionFsHeader& nstool::PfsProcess::getPfsHeader() const
{
return mPfs;
}
void PfsProcess::importHeader()
const std::shared_ptr<tc::io::IFileSystem>& nstool::PfsProcess::getFileSystem() const
{
fnd::Vec<byte_t> scratch;
if (*mFile == nullptr)
{
throw fnd::Exception(kModuleName, "No file reader set.");
}
// open minimum header to get full header size
scratch.alloc(sizeof(nn::hac::sPfsHeader));
(*mFile)->read(scratch.data(), 0, scratch.size());
if (validateHeaderMagic(((nn::hac::sPfsHeader*)scratch.data())) == false)
{
throw fnd::Exception(kModuleName, "Corrupt Header");
}
size_t pfsHeaderSize = determineHeaderSize(((nn::hac::sPfsHeader*)scratch.data()));
// open minimum header to get full header size
scratch.alloc(pfsHeaderSize);
(*mFile)->read(scratch.data(), 0, scratch.size());
mPfs.fromBytes(scratch.data(), scratch.size());
return mFileSystem;
}
void PfsProcess::displayHeader()
{
std::cout << "[PartitionFS]" << std::endl;
std::cout << " Type: " << nn::hac::PartitionFsUtil::getFsTypeAsString(mPfs.getFsType()) << std::endl;
std::cout << " FileNum: " << std::dec << mPfs.getFileList().size() << std::endl;
if (mMountName.empty() == false)
{
std::cout << " MountPoint: " << mMountName;
if (mMountName.at(mMountName.length()-1) != '/')
std::cout << "/";
std::cout << std::endl;
}
}
void PfsProcess::displayFs()
{
for (size_t i = 0; i < mPfs.getFileList().size(); i++)
{
const nn::hac::PartitionFsHeader::sFile& file = mPfs.getFileList()[i];
std::cout << " " << file.name;
if (_HAS_BIT(mCliOutputMode, OUTPUT_LAYOUT))
{
switch (mPfs.getFsType())
{
case (nn::hac::PartitionFsHeader::TYPE_PFS0):
std::cout << std::hex << " (offset=0x" << file.offset << ", size=0x" << file.size << ")";
break;
case (nn::hac::PartitionFsHeader::TYPE_HFS0):
std::cout << std::hex << " (offset=0x" << file.offset << ", size=0x" << file.size << ", hash_protected_size=0x" << file.hash_protected_size << ")";
break;
}
}
std::cout << std::endl;
}
}
size_t PfsProcess::determineHeaderSize(const nn::hac::sPfsHeader* hdr)
size_t nstool::PfsProcess::determineHeaderSize(const pie::hac::sPfsHeader* hdr)
{
size_t fileEntrySize = 0;
if (hdr->st_magic.get() == nn::hac::pfs::kPfsStructMagic)
fileEntrySize = sizeof(nn::hac::sPfsFile);
if (hdr->st_magic.unwrap() == pie::hac::pfs::kPfsStructMagic)
fileEntrySize = sizeof(pie::hac::sPfsFile);
else
fileEntrySize = sizeof(nn::hac::sHashedPfsFile);
fileEntrySize = sizeof(pie::hac::sHashedPfsFile);
return sizeof(nn::hac::sPfsHeader) + hdr->file_num.get() * fileEntrySize + hdr->name_table_size.get();
return sizeof(pie::hac::sPfsHeader) + hdr->file_num.unwrap() * fileEntrySize + hdr->name_table_size.unwrap();
}
bool PfsProcess::validateHeaderMagic(const nn::hac::sPfsHeader* hdr)
bool nstool::PfsProcess::validateHeaderMagic(const pie::hac::sPfsHeader* hdr)
{
return hdr->st_magic.get() == nn::hac::pfs::kPfsStructMagic || hdr->st_magic.get() == nn::hac::pfs::kHashedPfsStructMagic;
}
void PfsProcess::validateHfs()
{
fnd::sha::sSha256Hash hash;
const fnd::List<nn::hac::PartitionFsHeader::sFile>& file = mPfs.getFileList();
for (size_t i = 0; i < file.size(); i++)
{
mCache.alloc(file[i].hash_protected_size);
(*mFile)->read(mCache.data(), file[i].offset, file[i].hash_protected_size);
fnd::sha::Sha256(mCache.data(), file[i].hash_protected_size, hash.bytes);
if (hash != file[i].hash)
{
printf("[WARNING] HFS0 %s%s%s: FAIL (bad hash)\n", !mMountName.empty()? mMountName.c_str() : "", (!mMountName.empty() && mMountName.at(mMountName.length()-1) != '/' )? "/" : "", file[i].name.c_str());
}
}
}
void PfsProcess::extractFs()
{
// allocate only when extractDir is invoked
mCache.alloc(kCacheSize);
// make extract dir
fnd::io::makeDirectory(mExtractPath);
fnd::SimpleFile outFile;
const fnd::List<nn::hac::PartitionFsHeader::sFile>& file = mPfs.getFileList();
std::string file_path;
for (size_t i = 0; i < file.size(); i++)
{
file_path.clear();
fnd::io::appendToPath(file_path, mExtractPath);
fnd::io::appendToPath(file_path, file[i].name);
if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
printf("extract=[%s]\n", file_path.c_str());
outFile.open(file_path, outFile.Create);
(*mFile)->seek(file[i].offset);
for (size_t j = 0; j < ((file[i].size / kCacheSize) + ((file[i].size % kCacheSize) != 0)); j++)
{
(*mFile)->read(mCache.data(), _MIN(file[i].size - (kCacheSize * j),kCacheSize));
outFile.write(mCache.data(), _MIN(file[i].size - (kCacheSize * j),kCacheSize));
}
outFile.close();
}
return hdr->st_magic.unwrap() == pie::hac::pfs::kPfsStructMagic || hdr->st_magic.unwrap() == pie::hac::pfs::kHashedPfsStructMagic;
}

View file

@ -1,11 +1,10 @@
#pragma once
#include <string>
#include <fnd/types.h>
#include <fnd/IFile.h>
#include <fnd/SharedPtr.h>
#include <nn/hac/PartitionFsHeader.h>
#include "types.h"
#include "FsProcess.h"
#include "common.h"
#include <pietendo/hac/PartitionFsHeader.h>
namespace nstool {
class PfsProcess
{
@ -15,39 +14,35 @@ public:
void process();
// generic
void setInputFile(const fnd::SharedPtr<fnd::IFile>& file);
void setInputFile(const std::shared_ptr<tc::io::IStream>& file);
void setCliOutputMode(CliOutputMode type);
void setVerifyMode(bool verify);
// pfs specific
void setMountPointName(const std::string& mount_name);
void setExtractPath(const std::string& path);
void setListFs(bool list_fs);
// fs specific
void setShowFsTree(bool show_fs_tree);
void setFsRootLabel(const std::string& root_label);
void setExtractJobs(const std::vector<nstool::ExtractJob>& extract_jobs);
const nn::hac::PartitionFsHeader& getPfsHeader() const;
// post process() get PFS/FS out
const pie::hac::PartitionFsHeader& getPfsHeader() const;
const std::shared_ptr<tc::io::IFileSystem>& getFileSystem() const;
private:
const std::string kModuleName = "PfsProcess";
static const size_t kCacheSize = 0x10000;
fnd::SharedPtr<fnd::IFile> mFile;
std::string mModuleName;
std::shared_ptr<tc::io::IStream> mFile;
CliOutputMode mCliOutputMode;
bool mVerify;
std::string mExtractPath;
bool mExtract;
std::string mMountName;
bool mListFs;
pie::hac::PartitionFsHeader mPfs;
fnd::Vec<byte_t> mCache;
std::shared_ptr<tc::io::IFileSystem> mFileSystem;
FsProcess mFsProcess;
size_t determineHeaderSize(const pie::hac::sPfsHeader* hdr);
bool validateHeaderMagic(const pie::hac::sPfsHeader* hdr);
};
nn::hac::PartitionFsHeader mPfs;
void importHeader();
void displayHeader();
void displayFs();
size_t determineHeaderSize(const nn::hac::sPfsHeader* hdr);
bool validateHeaderMagic(const nn::hac::sPfsHeader* hdr);
void validateHfs();
void extractFs();
};
}

View file

@ -1,193 +0,0 @@
#include <iostream>
#include <iomanip>
#include <fnd/SimpleTextOutput.h>
#include <fnd/OffsetAdjustedIFile.h>
#include <nn/pki/SignUtils.h>
#include "PkiCertProcess.h"
#include "PkiValidator.h"
PkiCertProcess::PkiCertProcess() :
mFile(),
mCliOutputMode(_BIT(OUTPUT_BASIC)),
mVerify(false)
{
}
void PkiCertProcess::process()
{
importCerts();
if (mVerify)
validateCerts();
if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
displayCerts();
}
void PkiCertProcess::setInputFile(const fnd::SharedPtr<fnd::IFile>& file)
{
mFile = file;
}
void PkiCertProcess::setKeyCfg(const KeyConfiguration& keycfg)
{
mKeyCfg = keycfg;
}
void PkiCertProcess::setCliOutputMode(CliOutputMode mode)
{
mCliOutputMode = mode;
}
void PkiCertProcess::setVerifyMode(bool verify)
{
mVerify = verify;
}
void PkiCertProcess::importCerts()
{
fnd::Vec<byte_t> scratch;
if (*mFile == nullptr)
{
throw fnd::Exception(kModuleName, "No file reader set.");
}
scratch.alloc((*mFile)->size());
(*mFile)->read(scratch.data(), 0, scratch.size());
nn::pki::SignedData<nn::pki::CertificateBody> cert;
for (size_t f_pos = 0; f_pos < scratch.size(); f_pos += cert.getBytes().size())
{
cert.fromBytes(scratch.data() + f_pos, scratch.size() - f_pos);
mCert.addElement(cert);
}
}
void PkiCertProcess::validateCerts()
{
PkiValidator pki;
try
{
pki.setKeyCfg(mKeyCfg);
pki.addCertificates(mCert);
}
catch (const fnd::Exception& e)
{
std::cout << "[WARNING] " << e.error() << std::endl;
return;
}
}
void PkiCertProcess::displayCerts()
{
for (size_t i = 0; i < mCert.size(); i++)
{
displayCert(mCert[i]);
}
}
void PkiCertProcess::displayCert(const nn::pki::SignedData<nn::pki::CertificateBody>& cert)
{
std::cout << "[NNPKI Certificate]" << std::endl;
std::cout << " SignType " << getSignTypeStr(cert.getSignature().getSignType());
if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
std::cout << " (0x" << std::hex << cert.getSignature().getSignType() << ") (" << getEndiannessStr(cert.getSignature().isLittleEndian()) << ")";
std::cout << std::endl;
std::cout << " Issuer: " << cert.getBody().getIssuer() << std::endl;
std::cout << " Subject: " << cert.getBody().getSubject() << std::endl;
std::cout << " PublicKeyType: " << getPublicKeyTypeStr(cert.getBody().getPublicKeyType());
if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
std::cout << " (" << std::dec << cert.getBody().getPublicKeyType() << ")";
std::cout << std::endl;
std::cout << " CertID: 0x" << std::hex << cert.getBody().getCertId() << std::endl;
if (cert.getBody().getPublicKeyType() == nn::pki::cert::RSA4096)
{
std::cout << " PublicKey:" << std::endl;
std::cout << " Modulus:" << std::endl;
fnd::SimpleTextOutput::hexDump(cert.getBody().getRsa4098PublicKey().modulus, getHexDumpLen(fnd::rsa::kRsa4096Size), 0x10, 6);
std::cout << " Public Exponent:" << std::endl;
fnd::SimpleTextOutput::hexDump(cert.getBody().getRsa4098PublicKey().public_exponent, fnd::rsa::kRsaPublicExponentSize, 0x10, 6);
}
else if (cert.getBody().getPublicKeyType() == nn::pki::cert::RSA2048)
{
std::cout << " PublicKey:" << std::endl;
std::cout << " Modulus:" << std::endl;
fnd::SimpleTextOutput::hexDump(cert.getBody().getRsa2048PublicKey().modulus, getHexDumpLen(fnd::rsa::kRsa2048Size), 0x10, 6);
std::cout << " Public Exponent:" << std::endl;
fnd::SimpleTextOutput::hexDump(cert.getBody().getRsa2048PublicKey().public_exponent, fnd::rsa::kRsaPublicExponentSize, 0x10, 6);
}
else if (cert.getBody().getPublicKeyType() == nn::pki::cert::ECDSA240)
{
std::cout << " PublicKey:" << std::endl;
std::cout << " R:" << std::endl;
fnd::SimpleTextOutput::hexDump(cert.getBody().getEcdsa240PublicKey().r, getHexDumpLen(fnd::ecdsa::kEcdsa240Size), 0x10, 6);
std::cout << " S:" << std::endl;
fnd::SimpleTextOutput::hexDump(cert.getBody().getEcdsa240PublicKey().s, getHexDumpLen(fnd::ecdsa::kEcdsa240Size), 0x10, 6);
}
}
size_t PkiCertProcess::getHexDumpLen(size_t max_size) const
{
return _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED) ? max_size : kSmallHexDumpLen;
}
const char* PkiCertProcess::getSignTypeStr(nn::pki::sign::SignatureId type) const
{
const char* str;
switch (type)
{
case (nn::pki::sign::SIGN_ID_RSA4096_SHA1):
str = "RSA4096-SHA1";
break;
case (nn::pki::sign::SIGN_ID_RSA2048_SHA1):
str = "RSA2048-SHA1";
break;
case (nn::pki::sign::SIGN_ID_ECDSA240_SHA1):
str = "ECDSA240-SHA1";
break;
case (nn::pki::sign::SIGN_ID_RSA4096_SHA256):
str = "RSA4096-SHA256";
break;
case (nn::pki::sign::SIGN_ID_RSA2048_SHA256):
str = "RSA2048-SHA256";
break;
case (nn::pki::sign::SIGN_ID_ECDSA240_SHA256):
str = "ECDSA240-SHA256";
break;
default:
str = "Unknown";
break;
}
return str;
}
const char* PkiCertProcess::getEndiannessStr(bool isLittleEndian) const
{
return isLittleEndian ? "LittleEndian" : "BigEndian";
}
const char* PkiCertProcess::getPublicKeyTypeStr(nn::pki::cert::PublicKeyType type) const
{
const char* str;
switch (type)
{
case (nn::pki::cert::RSA4096):
str = "RSA4096";
break;
case (nn::pki::cert::RSA2048):
str = "RSA2048";
break;
case (nn::pki::cert::ECDSA240):
str = "ECDSA240";
break;
default:
str = "Unknown";
break;
}
return str;
}

View file

@ -1,45 +0,0 @@
#pragma once
#include <string>
#include <fnd/types.h>
#include <fnd/IFile.h>
#include <fnd/SharedPtr.h>
#include <fnd/List.h>
#include <fnd/Vec.h>
#include <nn/pki/SignedData.h>
#include <nn/pki/CertificateBody.h>
#include "KeyConfiguration.h"
#include "common.h"
class PkiCertProcess
{
public:
PkiCertProcess();
void process();
void setInputFile(const fnd::SharedPtr<fnd::IFile>& file);
void setKeyCfg(const KeyConfiguration& keycfg);
void setCliOutputMode(CliOutputMode type);
void setVerifyMode(bool verify);
private:
const std::string kModuleName = "PkiCertProcess";
static const size_t kSmallHexDumpLen = 0x10;
fnd::SharedPtr<fnd::IFile> mFile;
KeyConfiguration mKeyCfg;
CliOutputMode mCliOutputMode;
bool mVerify;
fnd::List<nn::pki::SignedData<nn::pki::CertificateBody>> mCert;
void importCerts();
void validateCerts();
void displayCerts();
void displayCert(const nn::pki::SignedData<nn::pki::CertificateBody>& cert);
size_t getHexDumpLen(size_t max_size) const;
const char* getSignTypeStr(nn::pki::sign::SignatureId type) const;
const char* getEndiannessStr(bool isLittleEndian) const;
const char* getPublicKeyTypeStr(nn::pki::cert::PublicKeyType type) const;
};

View file

@ -1,18 +1,19 @@
#include <iostream>
#include <iomanip>
#include <sstream>
#include <nn/pki/SignUtils.h>
#include "PkiValidator.h"
PkiValidator::PkiValidator()
#include <tc/crypto.h>
#include <pietendo/hac/define/types.h>
#include <pietendo/hac/es/SignUtils.h>
nstool::PkiValidator::PkiValidator() :
mModuleName("nstool::PkiValidator")
{
clearCertificates();
}
void PkiValidator::setKeyCfg(const KeyConfiguration& keycfg)
void nstool::PkiValidator::setKeyCfg(const KeyBag& keycfg)
{
// save a copy of the certificate bank
fnd::List<nn::pki::SignedData<nn::pki::CertificateBody>> old_certs = mCertificateBank;
std::vector<pie::hac::es::SignedData<pie::hac::es::CertificateBody>> old_certs = mCertificateBank;
// clear the certificate bank
mCertificateBank.clear();
@ -27,7 +28,7 @@ void PkiValidator::setKeyCfg(const KeyConfiguration& keycfg)
}
}
void PkiValidator::addCertificates(const fnd::List<nn::pki::SignedData<nn::pki::CertificateBody>>& certs)
void nstool::PkiValidator::addCertificates(const std::vector<pie::hac::es::SignedData<pie::hac::es::CertificateBody>>& certs)
{
for (size_t i = 0; i < certs.size(); i++)
{
@ -35,12 +36,11 @@ void PkiValidator::addCertificates(const fnd::List<nn::pki::SignedData<nn::pki::
}
}
void PkiValidator::addCertificate(const nn::pki::SignedData<nn::pki::CertificateBody>& cert)
void nstool::PkiValidator::addCertificate(const pie::hac::es::SignedData<pie::hac::es::CertificateBody>& cert)
{
std::string cert_ident;
nn::pki::sign::SignatureAlgo cert_sign_algo;
nn::pki::sign::HashAlgo cert_hash_algo;
fnd::Vec<byte_t> cert_hash;
pie::hac::es::sign::HashAlgo cert_hash_algo;
tc::ByteData cert_hash;
try
{
@ -48,121 +48,142 @@ void PkiValidator::addCertificate(const nn::pki::SignedData<nn::pki::Certificate
if (doesCertExist(cert_ident) == true)
{
throw fnd::Exception(kModuleName, "Certificate already exists");
throw tc::Exception(mModuleName, "Certificate already exists");
}
cert_sign_algo = nn::pki::sign::getSignatureAlgo(cert.getSignature().getSignType());
cert_hash_algo = nn::pki::sign::getHashAlgo(cert.getSignature().getSignType());
cert_hash_algo = pie::hac::es::sign::getHashAlgo(cert.getSignature().getSignType());
// get cert hash
switch (cert_hash_algo)
{
case (nn::pki::sign::HASH_ALGO_SHA1):
cert_hash.alloc(fnd::sha::kSha1HashLen);
fnd::sha::Sha1(cert.getBody().getBytes().data(), cert.getBody().getBytes().size(), cert_hash.data());
case (pie::hac::es::sign::HASH_ALGO_SHA1):
cert_hash = tc::ByteData(tc::crypto::Sha1Generator::kHashSize);
tc::crypto::GenerateSha1Hash(cert_hash.data(), cert.getBody().getBytes().data(), cert.getBody().getBytes().size());
break;
case (nn::pki::sign::HASH_ALGO_SHA256):
cert_hash.alloc(fnd::sha::kSha256HashLen);
fnd::sha::Sha256(cert.getBody().getBytes().data(), cert.getBody().getBytes().size(), cert_hash.data());
case (pie::hac::es::sign::HASH_ALGO_SHA256):
cert_hash = tc::ByteData(tc::crypto::Sha2256Generator::kHashSize);
tc::crypto::GenerateSha2256Hash(cert_hash.data(), cert.getBody().getBytes().data(), cert.getBody().getBytes().size());
break;
default:
throw fnd::Exception(kModuleName, "Unrecognised hash type");
throw tc::Exception(mModuleName, "Unrecognised hash type");
}
validateSignature(cert.getBody().getIssuer(), cert.getSignature().getSignType(), cert.getSignature().getSignature(), cert_hash);
mCertificateBank.addElement(cert);
mCertificateBank.push_back(cert);
}
catch (const fnd::Exception& e)
catch (const tc::Exception& e)
{
std::stringstream ss;
ss << "Failed to add certificate " << cert_ident << " (" << e.error() << ")";
throw fnd::Exception(kModuleName, ss.str());
throw tc::Exception(mModuleName, fmt::format("Failed to add certificate {:s} ({:s})", cert_ident, e.error()));
}
}
void PkiValidator::clearCertificates()
void nstool::PkiValidator::clearCertificates()
{
mCertificateBank.clear();
}
void PkiValidator::validateSignature(const std::string& issuer, nn::pki::sign::SignatureId signature_id, const fnd::Vec<byte_t>& signature, const fnd::Vec<byte_t>& hash) const
void nstool::PkiValidator::validateSignature(const std::string& issuer, pie::hac::es::sign::SignatureId signature_id, const tc::ByteData& signature, const tc::ByteData& hash) const
{
nn::pki::sign::SignatureAlgo sign_algo = nn::pki::sign::getSignatureAlgo(signature_id);
nn::pki::sign::HashAlgo hash_algo = nn::pki::sign::getHashAlgo(signature_id);
pie::hac::es::sign::SignatureAlgo sign_algo = pie::hac::es::sign::getSignatureAlgo(signature_id);
// validate signature
int sig_validate_res = -1;
bool sig_valid = false;
// special case if signed by Root
if (issuer.find('-', 0) == std::string::npos)
// get public key
// tc::crypto::EccKey ecc_key;
tc::crypto::RsaKey rsa_key;
// special case if signed by Root (legacy nstool only defers to keybag for "Root", it did not store certificates)
if (issuer == "Root")
{
fnd::rsa::sRsa4096Key rsa4096_pub;
fnd::rsa::sRsa2048Key rsa2048_pub;
fnd::ecdsa::sEcdsa240Key ecdsa_pub;
auto itr = mKeyCfg.broadon_signer.find(issuer);
if (mKeyCfg.getPkiRootSignKey(issuer, rsa4096_pub) == true && sign_algo == nn::pki::sign::SIGN_ALGO_RSA4096)
if (itr == mKeyCfg.broadon_signer.end())
{
sig_validate_res = fnd::rsa::pkcs::rsaVerify(rsa4096_pub, getCryptoHashAlgoFromEsSignHashAlgo(hash_algo), hash.data(), signature.data());
throw tc::Exception(mModuleName, fmt::format("Public key for issuer \"{:s}\" does not exist.", issuer));
}
else if (mKeyCfg.getPkiRootSignKey(issuer, rsa2048_pub) == true && sign_algo == nn::pki::sign::SIGN_ALGO_RSA2048)
if (sign_algo != itr->second.key_type)
{
sig_validate_res = fnd::rsa::pkcs::rsaVerify(rsa2048_pub, getCryptoHashAlgoFromEsSignHashAlgo(hash_algo), hash.data(), signature.data());
throw tc::Exception(mModuleName, fmt::format("Public key for issuer \"{:s}\" cannot verify this signature.", issuer));
}
else if (mKeyCfg.getPkiRootSignKey(issuer, ecdsa_pub) == true && sign_algo == nn::pki::sign::SIGN_ALGO_ECDSA240)
if (sign_algo == pie::hac::es::sign::SIGN_ALGO_ECDSA240)
{
throw fnd::Exception(kModuleName, "ECDSA signatures are not supported");
}
else
{
throw fnd::Exception(kModuleName, "Public key for issuer \"" + issuer + "\" does not exist.");
throw tc::Exception(mModuleName, "ECDSA signatures are not supported");
}
rsa_key = itr->second.rsa_key;
}
else
{
// try to find issuer cert
const nn::pki::CertificateBody& issuer_cert = getCert(issuer).getBody();
nn::pki::cert::PublicKeyType issuer_pubk_type = issuer_cert.getPublicKeyType();
const pie::hac::es::CertificateBody& issuer_cert = getCert(issuer).getBody();
pie::hac::es::cert::PublicKeyType issuer_pubk_type = issuer_cert.getPublicKeyType();
if (issuer_pubk_type == nn::pki::cert::RSA4096 && sign_algo == nn::pki::sign::SIGN_ALGO_RSA4096)
if (issuer_pubk_type == pie::hac::es::cert::RSA4096 && sign_algo == pie::hac::es::sign::SIGN_ALGO_RSA4096)
{
sig_validate_res = fnd::rsa::pkcs::rsaVerify(issuer_cert.getRsa4098PublicKey(), getCryptoHashAlgoFromEsSignHashAlgo(hash_algo), hash.data(), signature.data());
rsa_key = issuer_cert.getRsa4096PublicKey();
}
else if (issuer_pubk_type == nn::pki::cert::RSA2048 && sign_algo == nn::pki::sign::SIGN_ALGO_RSA2048)
else if (issuer_pubk_type == pie::hac::es::cert::RSA2048 && sign_algo == pie::hac::es::sign::SIGN_ALGO_RSA2048)
{
sig_validate_res = fnd::rsa::pkcs::rsaVerify(issuer_cert.getRsa2048PublicKey(), getCryptoHashAlgoFromEsSignHashAlgo(hash_algo), hash.data(), signature.data());
rsa_key = issuer_cert.getRsa2048PublicKey();
}
else if (issuer_pubk_type == nn::pki::cert::ECDSA240 && sign_algo == nn::pki::sign::SIGN_ALGO_ECDSA240)
else if (issuer_pubk_type == pie::hac::es::cert::ECDSA240 && sign_algo == pie::hac::es::sign::SIGN_ALGO_ECDSA240)
{
throw fnd::Exception(kModuleName, "ECDSA signatures are not supported");
// ecc_key = issuer_cert.getEcdsa240PublicKey();
throw tc::Exception(mModuleName, "ECDSA signatures are not supported");
}
else
{
throw fnd::Exception(kModuleName, "Mismatch between issuer public key and signature type");
throw tc::Exception(mModuleName, "Mismatch between issuer public key and signature type");
}
}
if (sig_validate_res != 0)
// verify signature
switch (signature_id) {
case (pie::hac::es::sign::SIGN_ID_RSA4096_SHA1):
sig_valid = tc::crypto::VerifyRsa4096Pkcs1Sha1(signature.data(), hash.data(), rsa_key);
break;
case (pie::hac::es::sign::SIGN_ID_RSA2048_SHA1):
sig_valid = tc::crypto::VerifyRsa2048Pkcs1Sha1(signature.data(), hash.data(), rsa_key);
break;
case (pie::hac::es::sign::SIGN_ID_ECDSA240_SHA1):
sig_valid = false;
break;
case (pie::hac::es::sign::SIGN_ID_RSA4096_SHA256):
sig_valid = tc::crypto::VerifyRsa4096Pkcs1Sha2256(signature.data(), hash.data(), rsa_key);
break;
case (pie::hac::es::sign::SIGN_ID_RSA2048_SHA256):
sig_valid = tc::crypto::VerifyRsa2048Pkcs1Sha2256(signature.data(), hash.data(), rsa_key);
break;
case (pie::hac::es::sign::SIGN_ID_ECDSA240_SHA256):
sig_valid = false;
break;
}
if (sig_valid == false)
{
throw fnd::Exception(kModuleName, "Incorrect signature");
throw tc::Exception(mModuleName, "Incorrect signature");
}
}
void PkiValidator::makeCertIdent(const nn::pki::SignedData<nn::pki::CertificateBody>& cert, std::string& ident) const
void nstool::PkiValidator::makeCertIdent(const pie::hac::es::SignedData<pie::hac::es::CertificateBody>& cert, std::string& ident) const
{
makeCertIdent(cert.getBody().getIssuer(), cert.getBody().getSubject(), ident);
}
void PkiValidator::makeCertIdent(const std::string& issuer, const std::string& subject, std::string& ident) const
void nstool::PkiValidator::makeCertIdent(const std::string& issuer, const std::string& subject, std::string& ident) const
{
ident = issuer + nn::pki::sign::kIdentDelimiter + subject;
ident = ident.substr(0, _MIN(ident.length(),64));
ident = issuer + pie::hac::es::sign::kIdentDelimiter + subject;
ident = ident.substr(0, std::min<size_t>(ident.length(),64));
}
bool PkiValidator::doesCertExist(const std::string& ident) const
bool nstool::PkiValidator::doesCertExist(const std::string& ident) const
{
bool exists = false;
std::string full_cert_name;
@ -179,7 +200,7 @@ bool PkiValidator::doesCertExist(const std::string& ident) const
return exists;
}
const nn::pki::SignedData<nn::pki::CertificateBody>& PkiValidator::getCert(const std::string& ident) const
const pie::hac::es::SignedData<pie::hac::es::CertificateBody>& nstool::PkiValidator::getCert(const std::string& ident) const
{
std::string full_cert_name;
for (size_t i = 0; i < mCertificateBank.size(); i++)
@ -191,22 +212,5 @@ const nn::pki::SignedData<nn::pki::CertificateBody>& PkiValidator::getCert(const
}
}
throw fnd::Exception(kModuleName, "Issuer certificate does not exist");
}
fnd::sha::HashType PkiValidator::getCryptoHashAlgoFromEsSignHashAlgo(nn::pki::sign::HashAlgo hash_algo) const
{
fnd::sha::HashType hash_type = fnd::sha::HASH_SHA1;
switch (hash_algo)
{
case (nn::pki::sign::HASH_ALGO_SHA1):
hash_type = fnd::sha::HASH_SHA1;
break;
case (nn::pki::sign::HASH_ALGO_SHA256):
hash_type = fnd::sha::HASH_SHA256;
break;
};
return hash_type;
throw tc::Exception(mModuleName, "Issuer certificate does not exist");
}

View file

@ -1,34 +1,34 @@
#pragma once
#include <fnd/types.h>
#include <fnd/List.h>
#include <fnd/Vec.h>
#include <fnd/rsa.h>
#include <nn/pki/SignedData.h>
#include <nn/pki/CertificateBody.h>
#include <string>
#include "KeyConfiguration.h"
#include "types.h"
#include "KeyBag.h"
#include <pietendo/hac/es/SignedData.h>
#include <pietendo/hac/es/CertificateBody.h>
namespace nstool {
class PkiValidator
{
public:
PkiValidator();
void setKeyCfg(const KeyConfiguration& keycfg);
void addCertificates(const fnd::List<nn::pki::SignedData<nn::pki::CertificateBody>>& certs);
void addCertificate(const nn::pki::SignedData<nn::pki::CertificateBody>& cert);
void setKeyCfg(const KeyBag& keycfg);
void addCertificates(const std::vector<pie::hac::es::SignedData<pie::hac::es::CertificateBody>>& certs);
void addCertificate(const pie::hac::es::SignedData<pie::hac::es::CertificateBody>& cert);
void clearCertificates();
void validateSignature(const std::string& issuer, nn::pki::sign::SignatureId signature_id, const fnd::Vec<byte_t>& signature, const fnd::Vec<byte_t>& hash) const;
void validateSignature(const std::string& issuer, pie::hac::es::sign::SignatureId signature_id, const tc::ByteData& signature, const tc::ByteData& hash) const;
private:
const std::string kModuleName = "NNPkiValidator";
std::string mModuleName;
KeyConfiguration mKeyCfg;
fnd::List<nn::pki::SignedData<nn::pki::CertificateBody>> mCertificateBank;
KeyBag mKeyCfg;
std::vector<pie::hac::es::SignedData<pie::hac::es::CertificateBody>> mCertificateBank;
void makeCertIdent(const nn::pki::SignedData<nn::pki::CertificateBody>& cert, std::string& ident) const;
void makeCertIdent(const pie::hac::es::SignedData<pie::hac::es::CertificateBody>& cert, std::string& ident) const;
void makeCertIdent(const std::string& issuer, const std::string& subject, std::string& ident) const;
bool doesCertExist(const std::string& ident) const;
const nn::pki::SignedData<nn::pki::CertificateBody>& getCert(const std::string& ident) const;
fnd::sha::HashType getCryptoHashAlgoFromEsSignHashAlgo(nn::pki::sign::HashAlgo hash_algo) const;
};
const pie::hac::es::SignedData<pie::hac::es::CertificateBody>& getCert(const std::string& ident) const;
};
}

View file

@ -1,12 +1,12 @@
#include "RoMetadataProcess.h"
#include <sstream>
#include <iostream>
#include <iomanip>
#include <fnd/types.h>
#include "RoMetadataProcess.h"
RoMetadataProcess::RoMetadataProcess() :
mCliOutputMode(_BIT(OUTPUT_BASIC)),
nstool::RoMetadataProcess::RoMetadataProcess() :
mModuleName("nstool::RoMetadataProcess"),
mCliOutputMode(true, false, false, false),
mIs64BitInstruction(true),
mListApi(false),
mListSymbols(false),
@ -23,90 +23,90 @@ RoMetadataProcess::RoMetadataProcess() :
}
void RoMetadataProcess::process()
void nstool::RoMetadataProcess::process()
{
importApiList();
if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
if (mCliOutputMode.show_basic_info)
displayRoMetaData();
}
void RoMetadataProcess::setRoBinary(const fnd::Vec<byte_t>& bin)
void nstool::RoMetadataProcess::setRoBinary(const tc::ByteData& bin)
{
mRoBlob = bin;
}
void RoMetadataProcess::setApiInfo(size_t offset, size_t size)
void nstool::RoMetadataProcess::setApiInfo(size_t offset, size_t size)
{
mApiInfo.offset = offset;
mApiInfo.size = size;
}
void RoMetadataProcess::setDynSym(size_t offset, size_t size)
void nstool::RoMetadataProcess::setDynSym(size_t offset, size_t size)
{
mDynSym.offset = offset;
mDynSym.size = size;
}
void RoMetadataProcess::setDynStr(size_t offset, size_t size)
void nstool::RoMetadataProcess::setDynStr(size_t offset, size_t size)
{
mDynStr.offset = offset;
mDynStr.size = size;
}
void RoMetadataProcess::setCliOutputMode(CliOutputMode type)
void nstool::RoMetadataProcess::setCliOutputMode(CliOutputMode type)
{
mCliOutputMode = type;
}
void RoMetadataProcess::setIs64BitInstruction(bool flag)
void nstool::RoMetadataProcess::setIs64BitInstruction(bool flag)
{
mIs64BitInstruction = flag;
}
void RoMetadataProcess::setListApi(bool listApi)
void nstool::RoMetadataProcess::setListApi(bool listApi)
{
mListApi = listApi;
}
void RoMetadataProcess::setListSymbols(bool listSymbols)
void nstool::RoMetadataProcess::setListSymbols(bool listSymbols)
{
mListSymbols = listSymbols;
}
const std::vector<SdkApiString>& RoMetadataProcess::getSdkVerApiList() const
const std::vector<nstool::SdkApiString>& nstool::RoMetadataProcess::getSdkVerApiList() const
{
return mSdkVerApiList;
}
const std::vector<SdkApiString>& RoMetadataProcess::getPublicApiList() const
const std::vector<nstool::SdkApiString>& nstool::RoMetadataProcess::getPublicApiList() const
{
return mPublicApiList;
}
const std::vector<SdkApiString>& RoMetadataProcess::getDebugApiList() const
const std::vector<nstool::SdkApiString>& nstool::RoMetadataProcess::getDebugApiList() const
{
return mDebugApiList;
}
const std::vector<SdkApiString>& RoMetadataProcess::getPrivateApiList() const
const std::vector<nstool::SdkApiString>& nstool::RoMetadataProcess::getPrivateApiList() const
{
return mPrivateApiList;
}
const std::vector<SdkApiString>& RoMetadataProcess::getGuidelineApiList() const
const std::vector<nstool::SdkApiString>& nstool::RoMetadataProcess::getGuidelineApiList() const
{
return mGuidelineApiList;
}
const fnd::List<ElfSymbolParser::sElfSymbol>& RoMetadataProcess::getSymbolList() const
const std::vector<nstool::ElfSymbolParser::sElfSymbol>& nstool::RoMetadataProcess::getSymbolList() const
{
return mSymbolList.getSymbolList();
}
void RoMetadataProcess::importApiList()
void nstool::RoMetadataProcess::importApiList()
{
if (mRoBlob.size() == 0)
{
throw fnd::Exception(kModuleName, "No ro binary set.");
throw tc::Exception(mModuleName, "No ro binary set.");
}
if (mApiInfo.size > 0)
@ -147,85 +147,85 @@ void RoMetadataProcess::importApiList()
}
}
void RoMetadataProcess::displayRoMetaData()
void nstool::RoMetadataProcess::displayRoMetaData()
{
size_t api_num = mSdkVerApiList.size() + mPublicApiList.size() + mDebugApiList.size() + mPrivateApiList.size();
if (api_num > 0 && (mListApi || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)))
if (api_num > 0 && (mListApi || mCliOutputMode.show_extended_info))
{
std::cout << "[SDK API List]" << std::endl;
fmt::print("[SDK API List]\n");
if (mSdkVerApiList.size() > 0)
{
std::cout << " Sdk Revision: " << mSdkVerApiList[0].getModuleName() << std::endl;
fmt::print(" Sdk Revision: {:s}\n", mSdkVerApiList[0].getModuleName());
}
if (mPublicApiList.size() > 0)
{
std::cout << " Public APIs:" << std::endl;
fmt::print(" Public APIs:\n");
for (size_t i = 0; i < mPublicApiList.size(); i++)
{
std::cout << " " << mPublicApiList[i].getModuleName() << " (vender: " << mPublicApiList[i].getVenderName() << ")" << std::endl;
fmt::print(" {:s} (vender: {:s})\n", mPublicApiList[i].getModuleName(), mPublicApiList[i].getVenderName());
}
}
if (mDebugApiList.size() > 0)
{
std::cout << " Debug APIs:" << std::endl;
fmt::print(" Debug APIs:\n");
for (size_t i = 0; i < mDebugApiList.size(); i++)
{
std::cout << " " << mDebugApiList[i].getModuleName() << " (vender: " << mDebugApiList[i].getVenderName() << ")" << std::endl;
fmt::print(" {:s} (vender: {:s})\n", mDebugApiList[i].getModuleName(), mDebugApiList[i].getVenderName());
}
}
if (mPrivateApiList.size() > 0)
{
std::cout << " Private APIs:" << std::endl;
fmt::print(" Private APIs:\n");
for (size_t i = 0; i < mPrivateApiList.size(); i++)
{
std::cout << " " << mPrivateApiList[i].getModuleName() << " (vender: " << mPrivateApiList[i].getVenderName() << ")" << std::endl;
fmt::print(" {:s} (vender: {:s})\n", mPrivateApiList[i].getModuleName(), mPrivateApiList[i].getVenderName());
}
}
if (mGuidelineApiList.size() > 0)
{
std::cout << " Guideline APIs:" << std::endl;
fmt::print(" Guideline APIs:\n");
for (size_t i = 0; i < mGuidelineApiList.size(); i++)
{
std::cout << " " << mGuidelineApiList[i].getModuleName() << " (vender: " << mGuidelineApiList[i].getVenderName() << ")" << std::endl;
fmt::print(" {:s} (vender: {:s})\n", mGuidelineApiList[i].getModuleName(), mGuidelineApiList[i].getVenderName());
}
}
}
if (mSymbolList.getSymbolList().size() > 0 && (mListSymbols || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)))
if (mSymbolList.getSymbolList().size() > 0 && (mListSymbols || mCliOutputMode.show_extended_info))
{
std::cout << "[Symbol List]" << std::endl;
fmt::print("[Symbol List]\n");
for (size_t i = 0; i < mSymbolList.getSymbolList().size(); i++)
{
const ElfSymbolParser::sElfSymbol& symbol = mSymbolList.getSymbolList()[i];
std::cout << " " << symbol.name << " [SHN=" << getSectionIndexStr(symbol.shn_index) << " (" << std::hex << std::setw(4) << std::setfill('0') << symbol.shn_index << ")][STT=" << getSymbolTypeStr(symbol.symbol_type) << "][STB=" << getSymbolBindingStr(symbol.symbol_binding) << "]" << std::endl;
fmt::print(" {:s} [SHN={:s} ({:04x})][STT={:s}][STB={:s}]\n", symbol.name, getSectionIndexStr(symbol.shn_index), symbol.shn_index, getSymbolTypeStr(symbol.symbol_type), getSymbolBindingStr(symbol.symbol_binding));
}
}
}
const char* RoMetadataProcess::getSectionIndexStr(uint16_t shn_index) const
std::string nstool::RoMetadataProcess::getSectionIndexStr(uint16_t shn_index) const
{
const char* str;
std::string str;
switch (shn_index)
{
case (fnd::elf::SHN_UNDEF):
case (elf::SHN_UNDEF):
str = "UNDEF";
break;
case (fnd::elf::SHN_LOPROC):
case (elf::SHN_LOPROC):
str = "LOPROC";
break;
case (fnd::elf::SHN_HIPROC):
case (elf::SHN_HIPROC):
str = "HIPROC";
break;
case (fnd::elf::SHN_LOOS):
case (elf::SHN_LOOS):
str = "LOOS";
break;
case (fnd::elf::SHN_HIOS):
case (elf::SHN_HIOS):
str = "HIOS";
break;
case (fnd::elf::SHN_ABS):
case (elf::SHN_ABS):
str = "ABS";
break;
case (fnd::elf::SHN_COMMON):
case (elf::SHN_COMMON):
str = "COMMON";
break;
default:
@ -235,36 +235,36 @@ const char* RoMetadataProcess::getSectionIndexStr(uint16_t shn_index) const
return str;
}
const char* RoMetadataProcess::getSymbolTypeStr(byte_t symbol_type) const
std::string nstool::RoMetadataProcess::getSymbolTypeStr(byte_t symbol_type) const
{
const char* str;
std::string str;
switch (symbol_type)
{
case (fnd::elf::STT_NOTYPE):
case (elf::STT_NOTYPE):
str = "NOTYPE";
break;
case (fnd::elf::STT_OBJECT):
case (elf::STT_OBJECT):
str = "OBJECT";
break;
case (fnd::elf::STT_FUNC):
case (elf::STT_FUNC):
str = "FUNC";
break;
case (fnd::elf::STT_SECTION):
case (elf::STT_SECTION):
str = "SECTION";
break;
case (fnd::elf::STT_FILE):
case (elf::STT_FILE):
str = "FILE";
break;
case (fnd::elf::STT_LOOS):
case (elf::STT_LOOS):
str = "LOOS";
break;
case (fnd::elf::STT_HIOS):
case (elf::STT_HIOS):
str = "HIOS";
break;
case (fnd::elf::STT_LOPROC):
case (elf::STT_LOPROC):
str = "LOPROC";
break;
case (fnd::elf::STT_HIPROC):
case (elf::STT_HIPROC):
str = "HIPROC";
break;
default:
@ -274,30 +274,30 @@ const char* RoMetadataProcess::getSymbolTypeStr(byte_t symbol_type) const
return str;
}
const char* RoMetadataProcess::getSymbolBindingStr(byte_t symbol_binding) const
std::string nstool::RoMetadataProcess::getSymbolBindingStr(byte_t symbol_binding) const
{
const char* str;
std::string str;
switch (symbol_binding)
{
case (fnd::elf::STB_LOCAL):
case (elf::STB_LOCAL):
str = "LOCAL";
break;
case (fnd::elf::STB_GLOBAL):
case (elf::STB_GLOBAL):
str = "GLOBAL";
break;
case (fnd::elf::STB_WEAK):
case (elf::STB_WEAK):
str = "WEAK";
break;
case (fnd::elf::STB_LOOS):
case (elf::STB_LOOS):
str = "LOOS";
break;
case (fnd::elf::STB_HIOS):
case (elf::STB_HIOS):
str = "HIOS";
break;
case (fnd::elf::STB_LOPROC):
case (elf::STB_LOPROC):
str = "LOPROC";
break;
case (fnd::elf::STB_HIPROC):
case (elf::STB_HIPROC):
str = "HIPROC";
break;
default:

View file

@ -1,15 +1,12 @@
#pragma once
#include <vector>
#include <string>
#include <fnd/types.h>
#include <fnd/Vec.h>
#include <nn/hac/define/meta.h>
#include "common.h"
#include "types.h"
#include "SdkApiString.h"
#include "ElfSymbolParser.h"
#include <pietendo/hac/define/meta.h>
namespace nstool {
class RoMetadataProcess
{
public:
@ -17,7 +14,7 @@ public:
void process();
void setRoBinary(const fnd::Vec<byte_t>& bin);
void setRoBinary(const tc::ByteData& bin);
void setApiInfo(size_t offset, size_t size);
void setDynSym(size_t offset, size_t size);
void setDynStr(size_t offset, size_t size);
@ -28,14 +25,14 @@ public:
void setListApi(bool listApi);
void setListSymbols(bool listSymbols);
const std::vector<SdkApiString>& getSdkVerApiList() const;
const std::vector<SdkApiString>& getPublicApiList() const;
const std::vector<SdkApiString>& getDebugApiList() const;
const std::vector<SdkApiString>& getPrivateApiList() const;
const std::vector<SdkApiString>& getGuidelineApiList() const;
const fnd::List<ElfSymbolParser::sElfSymbol>& getSymbolList() const;
const std::vector<nstool::SdkApiString>& getSdkVerApiList() const;
const std::vector<nstool::SdkApiString>& getPublicApiList() const;
const std::vector<nstool::SdkApiString>& getDebugApiList() const;
const std::vector<nstool::SdkApiString>& getPrivateApiList() const;
const std::vector<nstool::SdkApiString>& getGuidelineApiList() const;
const std::vector<nstool::ElfSymbolParser::sElfSymbol>& getSymbolList() const;
private:
const std::string kModuleName = "RoMetadataProcess";
std::string mModuleName;
CliOutputMode mCliOutputMode;
bool mIs64BitInstruction;
@ -52,7 +49,7 @@ private:
sLayout mApiInfo;
sLayout mDynSym;
sLayout mDynStr;
fnd::Vec<byte_t> mRoBlob;
tc::ByteData mRoBlob;
std::vector<SdkApiString> mSdkVerApiList;
std::vector<SdkApiString> mPublicApiList;
std::vector<SdkApiString> mDebugApiList;
@ -64,7 +61,9 @@ private:
void importApiList();
void displayRoMetaData();
const char* getSectionIndexStr(uint16_t shn_index) const;
const char* getSymbolTypeStr(byte_t symbol_type) const;
const char* getSymbolBindingStr(byte_t symbol_binding) const;
};
std::string getSectionIndexStr(uint16_t shn_index) const;
std::string getSymbolTypeStr(byte_t symbol_type) const;
std::string getSymbolBindingStr(byte_t symbol_binding) const;
};
}

View file

@ -1,341 +1,153 @@
#include <iostream>
#include <iomanip>
#include <fnd/SimpleTextOutput.h>
#include <fnd/SimpleFile.h>
#include <fnd/io.h>
#include "CompressedArchiveIFile.h"
#include "RomfsProcess.h"
#include "util.h"
RomfsProcess::RomfsProcess() :
#include <tc/io/VirtualFileSystem.h>
#include <pietendo/hac/RomFsSnapshotGenerator.h>
nstool::RomfsProcess::RomfsProcess() :
mModuleName("nstool::RomfsProcess"),
mFile(),
mCliOutputMode(_BIT(OUTPUT_BASIC)),
mCliOutputMode(true, false, false, false),
mVerify(false),
mExtractPath(),
mExtract(false),
mMountName(),
mListFs(false),
mDirNum(0),
mFileNum(0)
mFileNum(0),
mFileSystem(),
mFsProcess()
{
mRootDir.name.clear();
mRootDir.dir_list.clear();
mRootDir.file_list.clear();
mFsProcess.setFsFormatName("RomFs");
}
void RomfsProcess::process()
void nstool::RomfsProcess::process()
{
resolveRomfs();
if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
// state checks
if (mFile == nullptr)
{
displayHeader();
if (mListFs || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
displayFs();
throw tc::Exception(mModuleName, "No file reader set.");
}
if (mFile->canRead() == false || mFile->canSeek() == false)
{
throw tc::NotSupportedException(mModuleName, "Input stream requires read/seek permissions.");
}
if (mExtract)
extractFs();
// read base header to determine complete header size
if (mFile->length() < tc::io::IOUtil::castSizeToInt64(sizeof(pie::hac::sRomfsHeader)))
{
throw tc::Exception(mModuleName, "Corrupt RomFs: File too small");
}
mFile->seek(0, tc::io::SeekOrigin::Begin);
mFile->read((byte_t*)&mRomfsHeader, sizeof(mRomfsHeader));
if (mRomfsHeader.header_size.unwrap() != sizeof(pie::hac::sRomfsHeader) ||
mRomfsHeader.dir_entry.offset.unwrap() != (mRomfsHeader.dir_hash_bucket.offset.unwrap() + mRomfsHeader.dir_hash_bucket.size.unwrap()) ||
mRomfsHeader.data_offset.unwrap() != align<int64_t>(mRomfsHeader.header_size.unwrap(), pie::hac::romfs::kRomfsHeaderAlign))
{
throw tc::ArgumentOutOfRangeException(mModuleName, "Corrupt RomFs: RomFsHeader is corrupted.");
}
/*
fmt::print("RomFsHeader:\n");
fmt::print(" > header_size = 0x{:04x}\n", mRomfsHeader.header_size.unwrap());
fmt::print(" > dir_hash_bucket\n");
fmt::print(" > offset = 0x{:04x}\n", mRomfsHeader.dir_hash_bucket.offset.unwrap());
fmt::print(" > size = 0x{:04x}\n", mRomfsHeader.dir_hash_bucket.size.unwrap());
fmt::print(" > dir_entry\n");
fmt::print(" > offset = 0x{:04x}\n", mRomfsHeader.dir_entry.offset.unwrap());
fmt::print(" > size = 0x{:04x}\n", mRomfsHeader.dir_entry.size.unwrap());
fmt::print(" > file_hash_bucket\n");
fmt::print(" > offset = 0x{:04x}\n", mRomfsHeader.file_hash_bucket.offset.unwrap())
fmt::print(" > size = 0x{:04x}\n", mRomfsHeader.file_hash_bucket.size.unwrap());
fmt::print(" > file_entry\n");
fmt::print(" > offset = 0x{:04x}\n", mRomfsHeader.file_entry.offset.unwrap());
fmt::print(" > size = 0x{:04x}\n", mRomfsHeader.file_entry.size.unwrap());
fmt::print(" > data_offset = 0x{:04x}\n", mRomfsHeader.data_offset.unwrap());
*/
// get dir entry ptr
tc::ByteData dir_entry_table = tc::ByteData();
if (mRomfsHeader.dir_entry.size.unwrap() > 0)
{
dir_entry_table = tc::ByteData(tc::io::IOUtil::castInt64ToSize(mRomfsHeader.dir_entry.size.unwrap()));
mFile->seek(mRomfsHeader.dir_entry.offset.unwrap(), tc::io::SeekOrigin::Begin);
mFile->read(dir_entry_table.data(), dir_entry_table.size());
}
// get file entry ptr
tc::ByteData file_entry_table = tc::ByteData();
if (mRomfsHeader.file_entry.size.unwrap() > 0)
{
file_entry_table = tc::ByteData(tc::io::IOUtil::castInt64ToSize(mRomfsHeader.file_entry.size.unwrap()));
mFile->seek(mRomfsHeader.file_entry.offset.unwrap(), tc::io::SeekOrigin::Begin);
mFile->read(file_entry_table.data(), file_entry_table.size());
}
// count dir num
mDirNum = 0;
for (uint32_t v_addr = 0; size_t(v_addr) < dir_entry_table.size();)
{
uint32_t total_size = sizeof(pie::hac::sRomfsDirEntry) + align<uint32_t>(((pie::hac::sRomfsDirEntry*)(dir_entry_table.data() + v_addr))->name_size.unwrap(), 4);
// don't count root directory
if (v_addr != 0)
{
mDirNum += 1;
}
v_addr += total_size;
}
// count file num
mFileNum = 0;
for (uint32_t v_addr = 0; size_t(v_addr) < file_entry_table.size();)
{
uint32_t total_size = sizeof(pie::hac::sRomfsFileEntry) + align<uint32_t>(((pie::hac::sRomfsFileEntry*)(file_entry_table.data() + v_addr))->name_size.unwrap(), 4);
mFileNum += 1;
v_addr += total_size;
}
// create virtual filesystem
mFileSystem = std::make_shared<tc::io::VirtualFileSystem>(tc::io::VirtualFileSystem(pie::hac::RomFsSnapshotGenerator(mFile)));
mFsProcess.setInputFileSystem(mFileSystem);
// set properties for FsProcess
mFsProcess.setFsProperties({
fmt::format("DirNum: {:d}", mDirNum),
fmt::format("FileNum: {:d}", mFileNum)
});
// process filesystem
mFsProcess.process();
}
void RomfsProcess::setInputFile(const fnd::SharedPtr<fnd::IFile>& file)
void nstool::RomfsProcess::setInputFile(const std::shared_ptr<tc::io::IStream>& file)
{
mFile = file;
}
void RomfsProcess::setCliOutputMode(CliOutputMode type)
void nstool::RomfsProcess::setCliOutputMode(CliOutputMode type)
{
mCliOutputMode = type;
mFsProcess.setShowFsInfo(mCliOutputMode.show_basic_info);
}
void RomfsProcess::setVerifyMode(bool verify)
void nstool::RomfsProcess::setVerifyMode(bool verify)
{
mVerify = verify;
}
void RomfsProcess::setMountPointName(const std::string& mount_name)
void nstool::RomfsProcess::setFsRootLabel(const std::string& root_label)
{
mMountName = mount_name;
mFsProcess.setFsRootLabel(root_label);
}
void RomfsProcess::setExtractPath(const std::string& path)
void nstool::RomfsProcess::setExtractJobs(const std::vector<nstool::ExtractJob>& extract_jobs)
{
mExtract = true;
mExtractPath = path;
mFsProcess.setExtractJobs(extract_jobs);
}
void RomfsProcess::setListFs(bool list_fs)
void nstool::RomfsProcess::setShowFsTree(bool list_fs)
{
mListFs = list_fs;
}
const RomfsProcess::sDirectory& RomfsProcess::getRootDir() const
{
return mRootDir;
}
void RomfsProcess::printTab(size_t tab) const
{
for (size_t i = 0; i < tab; i++)
{
std::cout << " ";
}
}
void RomfsProcess::displayFile(const sFile& file, size_t tab) const
{
printTab(tab);
std::cout << file.name;
if (_HAS_BIT(mCliOutputMode, OUTPUT_LAYOUT))
{
std::cout << std::hex << " (offset=0x" << file.offset << ", size=0x" << file.size << ")";
}
std::cout << std::endl;
}
void RomfsProcess::displayDir(const sDirectory& dir, size_t tab) const
{
if (dir.name.empty() == false)
{
printTab(tab);
std::cout << dir.name << std::endl;
}
for (size_t i = 0; i < dir.dir_list.size(); i++)
{
displayDir(dir.dir_list[i], tab+1);
}
for (size_t i = 0; i < dir.file_list.size(); i++)
{
displayFile(dir.file_list[i], tab+1);
}
}
void RomfsProcess::displayHeader()
{
std::cout << "[RomFS]" << std::endl;
std::cout << " DirNum: " << std::dec << mDirNum << std::endl;
std::cout << " FileNum: " << std::dec << mFileNum << std::endl;
if (mMountName.empty() == false)
{
std::cout << " MountPoint: " << mMountName;
if (mMountName.at(mMountName.length()-1) != '/')
std::cout << "/";
std::cout << std::endl;
}
}
void RomfsProcess::displayFs()
{
displayDir(mRootDir, 1);
}
void RomfsProcess::extractDir(const std::string& path, const sDirectory& dir)
{
std::string dir_path;
std::string file_path;
// make dir path
fnd::io::appendToPath(dir_path, path);
if (dir.name.empty() == false)
fnd::io::appendToPath(dir_path, dir.name);
// make directory
fnd::io::makeDirectory(dir_path);
// extract files
fnd::SimpleFile outFile;
for (size_t i = 0; i < dir.file_list.size(); i++)
{
file_path.clear();
fnd::io::appendToPath(file_path, dir_path);
fnd::io::appendToPath(file_path, dir.file_list[i].name);
if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
std::cout << "extract=[" << file_path << "]" << std::endl;
outFile.open(file_path, outFile.Create);
(*mFile)->seek(dir.file_list[i].offset);
for (size_t j = 0; j < ((dir.file_list[i].size / kCacheSize) + ((dir.file_list[i].size % kCacheSize) != 0)); j++)
{
(*mFile)->read(mCache.data(), _MIN(dir.file_list[i].size - (kCacheSize * j),kCacheSize));
outFile.write(mCache.data(), _MIN(dir.file_list[i].size - (kCacheSize * j),kCacheSize));
}
outFile.close();
}
for (size_t i = 0; i < dir.dir_list.size(); i++)
{
extractDir(dir_path, dir.dir_list[i]);
}
}
void RomfsProcess::extractFs()
{
// allocate only when extractDir is invoked
mCache.alloc(kCacheSize);
extractDir(mExtractPath, mRootDir);
}
bool RomfsProcess::validateHeaderLayout(const nn::hac::sRomfsHeader* hdr) const
{
bool validLayout = true;
if (hdr->header_size.get() != sizeof(nn::hac::sRomfsHeader))
{
validLayout = false;
}
uint64_t pos = hdr->sections[0].offset.get();
for (size_t i = 0; i < nn::hac::romfs::SECTION_NUM; i++)
{
if (hdr->sections[i].offset.get() != pos)
{
validLayout = false;
}
pos += hdr->sections[i].size.get();
}
return validLayout;
}
void RomfsProcess::importDirectory(uint32_t dir_offset, sDirectory& dir)
{
nn::hac::sRomfsDirEntry* d_node = get_dir_node(dir_offset);
/*
printf("[DIR-NODE]\n");
printf(" parent=%08x\n", d_node->parent.get());
printf(" sibling=%08x\n", d_node->sibling.get());
printf(" child=%08x\n", d_node->child.get());
printf(" file=%08x\n", d_node->file.get());
printf(" hash=%08x\n", d_node->hash.get());
printf(" name_size=%08x\n", d_node->name_size.get());
printf(" name=%s\n", d_node->name);
*/
for (uint32_t file_addr = d_node->file.get(); file_addr != nn::hac::romfs::kInvalidAddr; )
{
nn::hac::sRomfsFileEntry* f_node = get_file_node(file_addr);
/*
printf("[FILE-NODE]\n");
printf(" parent=%08x\n", f_node->parent.get());
printf(" sibling=%08x\n", f_node->sibling.get());
printf(" offset=%08" PRIx64 "\n", f_node->offset.get());
printf(" size=%08" PRIx64 "\n", f_node->size.get());
printf(" hash=%08x\n", f_node->hash.get());
printf(" name_size=%08x\n", f_node->name_size.get());
printf(" name=%s\n", f_node->name);
*/
dir.file_list.addElement({std::string(f_node->name(), f_node->name_size.get()), mHdr.data_offset.get() + f_node->offset.get(), f_node->size.get()});
file_addr = f_node->sibling.get();
mFileNum++;
}
for (uint32_t child_addr = d_node->child.get(); child_addr != nn::hac::romfs::kInvalidAddr; )
{
nn::hac::sRomfsDirEntry* c_node = get_dir_node(child_addr);
dir.dir_list.addElement({std::string(c_node->name(), c_node->name_size.get())});
importDirectory(child_addr, dir.dir_list.atBack());
child_addr = c_node->sibling.get();
mDirNum++;
}
}
void RomfsProcess::resolveRomfs()
{
if (*mFile == nullptr)
{
throw fnd::Exception(kModuleName, "No file reader set.");
}
// read header
(*mFile)->read((byte_t*)&mHdr, 0, sizeof(nn::hac::sRomfsHeader));
// logic check on the header layout
if (validateHeaderLayout(&mHdr) == false)
{
throw fnd::Exception(kModuleName, "Invalid ROMFS Header");
}
// check for romfs compression
size_t physical_size = (*mFile)->size();
size_t logical_size = mHdr.sections[nn::hac::romfs::FILE_NODE_TABLE].offset.get() + mHdr.sections[nn::hac::romfs::FILE_NODE_TABLE].size.get();
// if logical size is greater than the physical size, check for compression meta footer
if (logical_size > physical_size)
{
// initial and final entries
nn::hac::sCompressionEntry entry[2];
// read final compression entry
(*mFile)->read((byte_t*)&entry[1], physical_size - sizeof(nn::hac::sCompressionEntry), sizeof(nn::hac::sCompressionEntry));
// the final compression entry should be for the (final part, in the case of metadata > 0x10000) romfs footer, for which the logical offset is detailed in the romfs header
// the compression is always enabled for non-header compression entries
uint64_t romfs_metadata_begin_offset = mHdr.sections[nn::hac::romfs::DIR_HASHMAP_TABLE].offset.get();
uint64_t romfs_metadata_end_offset = mHdr.sections[nn::hac::romfs::FILE_NODE_TABLE].offset.get() + mHdr.sections[nn::hac::romfs::FILE_NODE_TABLE].size.get();
if ((entry[1].virtual_offset.get() >= romfs_metadata_begin_offset && entry[1].virtual_offset.get() < romfs_metadata_end_offset) == false || \
entry[1].compression_type != (byte_t)nn::hac::compression::CompressionType::Lz4)
{
throw fnd::Exception(kModuleName, "RomFs appears corrupted (bad final compression entry virtual offset/compression type)");
}
// the first compression entry follows the physical placement of the final data chunk (specified in the final compression entry)
size_t first_entry_offset = align(entry[1].physical_offset.get() + entry[1].physical_size.get(), nn::hac::compression::kRomfsBlockAlign);
// quick check to make sure the offset at least before the last entry offset
if (first_entry_offset >= (physical_size - sizeof(nn::hac::sCompressionEntry)))
{
throw fnd::Exception(kModuleName, "RomFs appears corrupted (bad final compression entry physical offset/size)");
}
// read first compression entry
(*mFile)->read((byte_t*)&entry[0], first_entry_offset, sizeof(nn::hac::sCompressionEntry));
// validate first compression entry
// this should be the same for all compressed romfs
if (entry[0].virtual_offset.get() != 0x0 || \
entry[0].physical_offset.get() != 0x0 || \
entry[0].physical_size.get() != 0x200 || \
entry[0].compression_type != (byte_t)nn::hac::compression::CompressionType::None)
{
throw fnd::Exception(kModuleName, "RomFs appears corrupted (bad first compression entry)");
}
// wrap mFile in a class to transparantly decompress the image.
mFile = new CompressedArchiveIFile(mFile, first_entry_offset);
}
// read directory nodes
mDirNodes.alloc(mHdr.sections[nn::hac::romfs::DIR_NODE_TABLE].size.get());
(*mFile)->read(mDirNodes.data(), mHdr.sections[nn::hac::romfs::DIR_NODE_TABLE].offset.get(), mDirNodes.size());
//printf("[RAW DIR NODES]\n");
//fnd::SimpleTextOutput::hxdStyleDump(mDirNodes.data(), mDirNodes.size());
// read file nodes
mFileNodes.alloc(mHdr.sections[nn::hac::romfs::FILE_NODE_TABLE].size.get());
(*mFile)->read(mFileNodes.data(), mHdr.sections[nn::hac::romfs::FILE_NODE_TABLE].offset.get(), mFileNodes.size());
//printf("[RAW FILE NODES]\n");
//fnd::SimpleTextOutput::hxdStyleDump(mFileNodes.data(), mFileNodes.size());
// A logic check on the root directory node
if ( get_dir_node(0)->parent.get() != 0 \
|| get_dir_node(0)->sibling.get() != nn::hac::romfs::kInvalidAddr \
|| get_dir_node(0)->hash.get() != nn::hac::romfs::kInvalidAddr \
|| get_dir_node(0)->name_size.get() != 0)
{
throw fnd::Exception(kModuleName, "Invalid root directory node");
}
// import directory into internal structure
mDirNum = 0;
mFileNum = 0;
importDirectory(0, mRootDir);
mFsProcess.setShowFsTree(list_fs);
}

View file

@ -1,134 +1,42 @@
#pragma once
#include <string>
#include <fnd/types.h>
#include <fnd/IFile.h>
#include <fnd/SharedPtr.h>
#include <fnd/Vec.h>
#include <fnd/List.h>
#include <nn/hac/define/romfs.h>
#include "types.h"
#include "FsProcess.h"
#include "common.h"
#include <pietendo/hac/define/romfs.h>
namespace nstool {
class RomfsProcess
{
public:
struct sDirectory;
struct sFile;
struct sDirectory
{
std::string name;
fnd::List<sDirectory> dir_list;
fnd::List<sFile> file_list;
void operator=(const sDirectory& other)
{
name = other.name;
dir_list = other.dir_list;
file_list = other.file_list;
}
bool operator==(const sDirectory& other) const
{
return (name == other.name) \
&& (dir_list == other.dir_list) \
&& (file_list == other.file_list);
}
bool operator!=(const sDirectory& other) const
{
return !operator==(other);
}
bool operator==(const std::string& other) const
{
return (name == other);
}
};
struct sFile
{
std::string name;
uint64_t offset;
uint64_t size;
void operator=(const sFile& other)
{
name = other.name;
offset = other.offset;
size = other.size;
}
bool operator==(const sFile& other) const
{
return (name == other.name) \
&& (offset == other.offset) \
&& (size == other.size);
}
bool operator!=(const sFile& other) const
{
return !operator==(other);
}
bool operator==(const std::string& other) const
{
return (name == other);
}
};
RomfsProcess();
void process();
// generic
void setInputFile(const fnd::SharedPtr<fnd::IFile>& file);
void setInputFile(const std::shared_ptr<tc::io::IStream>& file);
void setCliOutputMode(CliOutputMode type);
void setVerifyMode(bool verify);
// romfs specific
void setMountPointName(const std::string& mount_name);
void setExtractPath(const std::string& path);
void setListFs(bool list_fs);
const sDirectory& getRootDir() const;
// fs specific
void setFsRootLabel(const std::string& root_label);
void setExtractJobs(const std::vector<nstool::ExtractJob>& extract_jobs);
void setShowFsTree(bool show_fs_tree);
private:
const std::string kModuleName = "RomfsProcess";
static const size_t kCacheSize = 0x10000;
fnd::SharedPtr<fnd::IFile> mFile;
std::string mModuleName;
std::shared_ptr<tc::io::IStream> mFile;
CliOutputMode mCliOutputMode;
bool mVerify;
std::string mExtractPath;
bool mExtract;
std::string mMountName;
bool mListFs;
fnd::Vec<byte_t> mCache;
pie::hac::sRomfsHeader mRomfsHeader;
size_t mDirNum;
size_t mFileNum;
nn::hac::sRomfsHeader mHdr;
fnd::Vec<byte_t> mDirNodes;
fnd::Vec<byte_t> mFileNodes;
sDirectory mRootDir;
inline nn::hac::sRomfsDirEntry* get_dir_node(uint32_t offset) { return (nn::hac::sRomfsDirEntry*)(mDirNodes.data() + offset); }
inline nn::hac::sRomfsFileEntry* get_file_node(uint32_t offset) { return (nn::hac::sRomfsFileEntry*)(mFileNodes.data() + offset); }
std::shared_ptr<tc::io::IFileSystem> mFileSystem;
FsProcess mFsProcess;
};
void printTab(size_t tab) const;
void displayFile(const sFile& file, size_t tab) const;
void displayDir(const sDirectory& dir, size_t tab) const;
void displayHeader();
void displayFs();
void extractDir(const std::string& path, const sDirectory& dir);
void extractFs();
bool validateHeaderLayout(const nn::hac::sRomfsHeader* hdr) const;
void importDirectory(uint32_t dir_offset, sDirectory& dir);
void resolveRomfs();
};
}

View file

@ -1,13 +1,13 @@
#include <sstream>
#include "SdkApiString.h"
SdkApiString::SdkApiString(const std::string& full_str) :
nstool::SdkApiString::SdkApiString(const std::string& full_str) :
SdkApiString(API_MIDDLEWARE, "", "")
{
resolveApiString(full_str);
}
SdkApiString::SdkApiString(ApiType type, const std::string& vender_name, const std::string& module_name) :
nstool::SdkApiString::SdkApiString(ApiType type, const std::string& vender_name, const std::string& module_name) :
mApiType(type),
mVenderName(vender_name),
mModuleName(module_name)
@ -15,44 +15,44 @@ SdkApiString::SdkApiString(ApiType type, const std::string& vender_name, const s
}
void SdkApiString::operator=(const SdkApiString& other)
void nstool::SdkApiString::operator=(const SdkApiString& other)
{
mApiType = other.mApiType;
mVenderName = other.mVenderName;
mModuleName = other.mModuleName;
}
SdkApiString::ApiType SdkApiString::getApiType() const
nstool::SdkApiString::ApiType nstool::SdkApiString::getApiType() const
{
return mApiType;
}
void SdkApiString::setApiType(ApiType type)
void nstool::SdkApiString::setApiType(ApiType type)
{
mApiType = type;
}
const std::string& SdkApiString::getVenderName() const
const std::string& nstool::SdkApiString::getVenderName() const
{
return mVenderName;
}
void SdkApiString::setVenderName(const std::string& name)
void nstool::SdkApiString::setVenderName(const std::string& name)
{
mVenderName = name;
}
const std::string& SdkApiString::getModuleName() const
const std::string& nstool::SdkApiString::getModuleName() const
{
return mModuleName;
}
void SdkApiString::setModuleName(const std::string& name)
void nstool::SdkApiString::setModuleName(const std::string& name)
{
mModuleName = name;
}
void SdkApiString::resolveApiString(const std::string& full_str)
void nstool::SdkApiString::resolveApiString(const std::string& full_str)
{
std::stringstream list_stream(full_str);
std::string api_type, vender, module;

View file

@ -1,5 +1,7 @@
#pragma once
#include <string>
#include "types.h"
namespace nstool {
class SdkApiString
{
@ -42,4 +44,6 @@ private:
std::string mModuleName;
void resolveApiString(const std::string& full_str);
};
};
}

1187
src/Settings.cpp Normal file

File diff suppressed because it is too large Load diff

155
src/Settings.h Normal file
View file

@ -0,0 +1,155 @@
#pragma once
#include "types.h"
#include <string>
#include <vector>
#include <tc/Optional.h>
#include <tc/io.h>
#include "KeyBag.h"
namespace nstool {
struct Settings
{
enum FileType
{
FILE_TYPE_ERROR,
FILE_TYPE_GAMECARD,
FILE_TYPE_NSP,
FILE_TYPE_PARTITIONFS,
FILE_TYPE_ROMFS,
FILE_TYPE_NCA,
FILE_TYPE_META,
FILE_TYPE_CNMT,
FILE_TYPE_NSO,
FILE_TYPE_NRO,
FILE_TYPE_NACP,
FILE_TYPE_INI,
FILE_TYPE_KIP,
FILE_TYPE_ES_CERT,
FILE_TYPE_ES_TIK,
FILE_TYPE_HB_ASSET,
};
struct InputFileOptions
{
FileType filetype;
tc::Optional<tc::io::Path> path;
} infile;
struct Options
{
CliOutputMode cli_output_mode;
bool verify;
bool is_dev;
KeyBag keybag;
} opt;
// code options
struct CodeOptions
{
bool list_api;
bool list_symbols;
bool is_64bit_instruction; // true=64bit, false=32bit
} code;
// Generic FS options
struct FsOptions
{
bool show_fs_tree;
std::vector<ExtractJob> extract_jobs;
} fs;
// XCI options
struct XciOptions
{
tc::Optional<tc::io::Path> update_extract_path;
tc::Optional<tc::io::Path> logo_extract_path;
tc::Optional<tc::io::Path> normal_extract_path;
tc::Optional<tc::io::Path> secure_extract_path;
} xci;
// NCA options
struct NcaOptions
{
tc::Optional<tc::io::Path> part0_extract_path;
tc::Optional<tc::io::Path> part1_extract_path;
tc::Optional<tc::io::Path> part2_extract_path;
tc::Optional<tc::io::Path> part3_extract_path;
tc::Optional<tc::io::Path> base_nca_path;
} nca;
// KIP options
struct KipOptions
{
tc::Optional<tc::io::Path> extract_path;
} kip;
// ASET Options
struct AsetOptions
{
tc::Optional<tc::io::Path> icon_extract_path;
tc::Optional<tc::io::Path> nacp_extract_path;
} aset;
Settings()
{
infile.filetype = FILE_TYPE_ERROR;
infile.path = tc::Optional<tc::io::Path>();
opt.cli_output_mode = CliOutputMode();
opt.verify = false;
opt.is_dev = false;
opt.keybag = KeyBag();
code.list_api = false;
code.list_symbols = false;
code.is_64bit_instruction = true;
fs.show_fs_tree = false;
fs.extract_jobs = std::vector<ExtractJob>();
kip.extract_path = tc::Optional<tc::io::Path>();
nca.base_nca_path = tc::Optional<tc::io::Path>();
aset.icon_extract_path = tc::Optional<tc::io::Path>();
aset.nacp_extract_path = tc::Optional<tc::io::Path>();
}
};
class SettingsInitializer : public Settings
{
public:
SettingsInitializer(const std::vector<std::string>& args);
private:
void parse_args(const std::vector<std::string>& args);
void determine_filetype();
void usage_text() const;
void dump_keys() const;
void dump_rsa_key(const KeyBag::rsa_key_t& key, const std::string& label, size_t indent, bool expanded_key_data) const;
std::string mModuleLabel;
bool mShowLayout;
bool mShowKeydata;
bool mVerbose;
tc::Optional<tc::io::Path> mKeysetPath;
tc::Optional<tc::io::Path> mTitleKeysetPath;
tc::Optional<KeyBag::aes128_key_t> mNcaEncryptedContentKey;
tc::Optional<KeyBag::aes128_key_t> mNcaContentKey;
std::vector<tc::io::Path> mTikPathList;
//tc::Optional<tc::io::Path> mTikPath;
tc::Optional<tc::io::Path> mCertPath;
void loadKeyFile(tc::Optional<tc::io::Path>& keyfile_path, const std::string& keyfile_name, const std::string& cli_hint);
bool determineValidNcaFromSample(const tc::ByteData& raw_data) const;
bool determineValidEsCertFromSample(const tc::ByteData& raw_data) const;
bool determineValidEsTikFromSample(const tc::ByteData& raw_data) const;
bool determineValidCnmtFromSample(const tc::ByteData& raw_data) const;
bool determineValidNacpFromSample(const tc::ByteData& raw_data) const;
};
}

File diff suppressed because it is too large Load diff

View file

@ -1,138 +0,0 @@
#pragma once
#include <vector>
#include <string>
#include <fnd/types.h>
#include <fnd/Vec.h>
#include <fnd/List.h>
#include <nn/pki/SignedData.h>
#include <nn/pki/CertificateBody.h>
#include <nn/hac/define/meta.h>
#include "common.h"
#include "KeyConfiguration.h"
class UserSettings
{
public:
UserSettings();
void parseCmdArgs(const std::vector<std::string>& arg_list);
void showHelp();
// generic options
const std::string getInputPath() const;
const KeyConfiguration& getKeyCfg() const;
FileType getFileType() const;
bool isVerifyFile() const;
CliOutputMode getCliOutputMode() const;
// specialised toggles
bool isListFs() const;
bool isListApi() const;
bool isListSymbols() const;
bool getIs64BitInstruction() const;
// specialised paths
const sOptional<std::string>& getXciUpdatePath() const;
const sOptional<std::string>& getXciLogoPath() const;
const sOptional<std::string>& getXciNormalPath() const;
const sOptional<std::string>& getXciSecurePath() const;
const sOptional<std::string>& getFsPath() const;
const sOptional<std::string>& getNcaPart0Path() const;
const sOptional<std::string>& getNcaPart1Path() const;
const sOptional<std::string>& getNcaPart2Path() const;
const sOptional<std::string>& getNcaPart3Path() const;
const sOptional<std::string>& getKipExtractPath() const;
const sOptional<std::string>& getAssetIconPath() const;
const sOptional<std::string>& getAssetNacpPath() const;
const fnd::List<nn::pki::SignedData<nn::pki::CertificateBody>>& getCertificateChain() const;
private:
const std::string kModuleName = "UserSettings";
const std::string kHomeSwitchDirStr = ".switch";
const std::string kGeneralKeyfileName[2] = { "prod.keys", "dev.keys" };
const std::string kTitleKeyfileName = "title.keys";
struct sCmdArgs
{
sCmdArgs() {}
sOptional<std::string> input_path;
sOptional<bool> devkit_keys;
sOptional<std::string> keyset_path;
sOptional<std::string> file_type;
sOptional<bool> verify_file;
sOptional<bool> show_keys;
sOptional<bool> show_layout;
sOptional<bool> verbose_output;
sOptional<bool> list_fs;
sOptional<std::string> update_path;
sOptional<std::string> logo_path;
sOptional<std::string> normal_path;
sOptional<std::string> secure_path;
sOptional<std::string> fs_path;
sOptional<std::string> nca_titlekey;
sOptional<std::string> nca_bodykey;
sOptional<std::string> ticket_path;
sOptional<std::string> cert_path;
sOptional<std::string> part0_path;
sOptional<std::string> part1_path;
sOptional<std::string> part2_path;
sOptional<std::string> part3_path;
sOptional<std::string> kip_extract_path;
sOptional<bool> list_api;
sOptional<bool> list_sym;
sOptional<std::string> inst_type;
sOptional<std::string> asset_icon_path;
sOptional<std::string> asset_nacp_path;
};
std::string mInputPath;
FileType mFileType;
KeyConfiguration mKeyCfg;
bool mVerifyFile;
CliOutputMode mOutputMode;
bool mListFs;
sOptional<std::string> mXciUpdatePath;
sOptional<std::string> mXciLogoPath;
sOptional<std::string> mXciNormalPath;
sOptional<std::string> mXciSecurePath;
sOptional<std::string> mFsPath;
sOptional<std::string> mNcaPart0Path;
sOptional<std::string> mNcaPart1Path;
sOptional<std::string> mNcaPart2Path;
sOptional<std::string> mNcaPart3Path;
sOptional<std::string> mKipExtractPath;
sOptional<std::string> mAssetIconPath;
sOptional<std::string> mAssetNacpPath;
fnd::List<nn::pki::SignedData<nn::pki::CertificateBody>> mCertChain;
bool mListApi;
bool mListSymbols;
bool mIs64BitInstruction;
void populateCmdArgs(const std::vector<std::string>& arg_list, sCmdArgs& cmd_args);
void populateKeyset(sCmdArgs& args);
void populateUserSettings(sCmdArgs& args);
FileType getFileTypeFromString(const std::string& type_str);
FileType determineFileTypeFromFile(const std::string& path);
bool determineValidNcaFromSample(const fnd::Vec<byte_t>& sample) const;
bool determineValidCnmtFromSample(const fnd::Vec<byte_t>& sample) const;
bool determineValidNacpFromSample(const fnd::Vec<byte_t>& sample) const;
bool determineValidEsCertFromSample(const fnd::Vec<byte_t>& sample) const;
bool determineValidEsTikFromSample(const fnd::Vec<byte_t>& sample) const;
bool getIs64BitInstructionFromString(const std::string& type_str);
void getHomePath(std::string& path) const;
void getSwitchPath(std::string& path) const;
void dumpKeyConfig() const;
void dumpRsa2048Key(const fnd::rsa::sRsa2048Key& key, const std::string& name, size_t indent) const;
void dumpRsa4096Key(const fnd::rsa::sRsa4096Key& key, const std::string& name, size_t indent) const;
void dumpAesKey(const fnd::aes::sAes128Key& key, const std::string& name, size_t indent) const;
void dumpAesXtsKey(const fnd::aes::sAesXts128Key& key, const std::string& name, size_t indent) const;
};

View file

@ -1,62 +0,0 @@
#pragma once
#include <string>
#include <fnd/types.h>
#include <fnd/aes.h>
#include <fnd/rsa.h>
#include <nn/hac/define/nca.h>
static const size_t kMasterKeyNum = 0x20;
static const size_t kNcaKeakNum = nn::hac::nca::kKeyAreaEncryptionKeyNum;
enum FileType
{
FILE_GAMECARD,
FILE_NSP,
FILE_PARTITIONFS,
FILE_ROMFS,
FILE_NCA,
FILE_META,
FILE_CNMT,
FILE_NSO,
FILE_NRO,
FILE_NACP,
FILE_INI,
FILE_KIP,
FILE_PKI_CERT,
FILE_ES_TIK,
FILE_HB_ASSET,
FILE_INVALID = -1,
};
enum CliOutputModeFlag
{
OUTPUT_BASIC,
OUTPUT_LAYOUT,
OUTPUT_KEY_DATA,
OUTPUT_EXTENDED
};
typedef byte_t CliOutputMode;
template <typename T>
struct sOptional
{
bool isSet;
T var;
inline sOptional() : isSet(false) {}
inline sOptional(const T& other) : isSet(true), var(other) {}
inline sOptional(const sOptional& other) : isSet(other.isSet), var(other.var) {}
inline const T& operator=(const T& other) { isSet = true; var = other; return var; }
inline const sOptional<T>& operator=(const sOptional<T>& other)
{
isSet = other.isSet;
if (isSet) {
var = other.var;
}
return *this;
}
inline T& operator*() { return var; }
};
const byte_t kDummyRightsIdForUserTitleKey[nn::hac::nca::kRightsIdLen] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
const byte_t kDummyRightsIdForUserBodyKey[nn::hac::nca::kRightsIdLen] = {0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe, 0xfe};

458
src/elf.h Normal file
View file

@ -0,0 +1,458 @@
#pragma once
#include "types.h"
namespace nstool
{
namespace elf
{
/* These constants are for the segment types stored in the image headers */
enum SegmentType
{
PT_NULL = 0,
PT_LOAD = 1,
PT_DYNAMIC = 2,
PT_INTERP = 3,
PT_NOTE = 4,
PT_SHLIB = 5,
PT_PHDR = 6,
PT_TLS = 7, /* Thread local storage segment */
PT_LOOS = 0x60000000, /* OS-specific */
PT_HIOS = 0x6fffffff, /* OS-specific */
PT_LOPROC = 0x70000000,
PT_HIPROC = 0x7fffffff
};
/* These constants define the different elf file types */
enum ElfType
{
ET_NONE = 0,
ET_REL = 1,
ET_EXEC = 2,
ET_DYN = 3,
ET_CORE = 4,
ET_LOPROC = 0xff00,
ET_HIPROC = 0xffff
};
/* This is the info that is needed to parse the dynamic section of the file */
enum DynamicSectionType
{
DT_NULL = 0,
DT_NEEDED = 1,
DT_PLTRELSZ = 2,
DT_PLTGOT = 3,
DT_HASH = 4,
DT_STRTAB = 5,
DT_SYMTAB = 6,
DT_RELA = 7,
DT_RELASZ = 8,
DT_RELAENT = 9,
DT_STRSZ = 10,
DT_SYMENT = 11,
DT_INIT = 12,
DT_FINI = 13,
DT_SONAME = 14,
DT_RPATH = 15,
DT_SYMBOLIC = 16,
DT_REL = 17,
DT_RELSZ = 18,
DT_RELENT = 19,
DT_PLTREL = 20,
DT_DEBUG = 21,
DT_TEXTREL = 22,
DT_JMPREL = 23,
DT_ENCODING = 32,
OLD_DT_LOOS = 0x60000000,
DT_LOOS = 0x6000000d,
DT_HIOS = 0x6ffff000,
DT_VALRNGLO = 0x6ffffd00,
DT_VALRNGHI = 0x6ffffdff,
DT_ADDRRNGLO = 0x6ffffe00,
DT_ADDRRNGHI = 0x6ffffeff,
DT_VERSYM = 0x6ffffff0,
DT_RELACOUNT = 0x6ffffff9,
DT_RELCOUNT = 0x6ffffffa,
DT_FLAGS_1 = 0x6ffffffb,
DT_VERDEF = 0x6ffffffc,
DT_VERDEFNUM = 0x6ffffffd,
DT_VERNEED = 0x6ffffffe,
DT_VERNEEDNUM = 0x6fffffff,
OLD_DT_HIOS = 0x6fffffff,
DT_LOPROC = 0x70000000,
DT_HIPROC = 0x7fffffff
};
/* This info is needed when parsing the symbol table */
enum SymbolBinding
{
STB_LOCAL = 0,
STB_GLOBAL = 1,
STB_WEAK = 2,
STB_LOOS = 10,
STB_HIOS = 12,
STB_LOPROC,
STB_HIPROC = 0xf
};
enum SymbolType
{
STT_NOTYPE = 0,
STT_OBJECT = 1,
STT_FUNC = 2,
STT_SECTION = 3,
STT_FILE = 4,
STT_COMMON = 5,
STT_TLS = 6,
STT_LOOS = 10,
STT_HIOS = 12,
STT_LOPROC,
STT_HIPROC = 0xf
};
/* These constants define the permissions on sections in the program
header, p_flags. */
enum PermissionFlag
{
PF_R = 0x4,
PF_W = 0x2,
PF_X = 0x1
};
/* sh_type */
enum SectionHeaderType
{
SHT_NULL = 0,
SHT_PROGBITS = 1,
SHT_SYMTAB = 2,
SHT_STRTAB = 3,
SHT_RELA = 4,
SHT_HASH = 5,
SHT_DYNAMIC = 6,
SHT_NOTE = 7,
SHT_NOBITS = 8,
SHT_REL = 9,
SHT_SHLIB = 10,
SHT_DYNSYM = 11,
SHT_NUM = 12,
SHT_LOPROC = 0x70000000,
SHT_HIPROC = 0x7fffffff,
SHT_LOUSER = 0x80000000,
SHT_HIUSER = 0xffffffff
};
/* sh_flags */
enum SectionHeaderFlag
{
SHF_WRITE = 0x1,
SHF_ALLOC = 0x2,
SHF_EXECINSTR = 0x4,
SHF_RELA_LIVEPATCH = 0x00100000,
SHF_RO_AFTER_INIT = 0x00200000,
SHF_MASKPROC = 0xf0000000
};
/* special section indexes */
enum SpecialSectionIndex
{
SHN_UNDEF = 0,
SHN_LORESERVE = 0xff00,
SHN_LOPROC = 0xff00,
SHN_HIPROC = 0xff1f,
SHN_LOOS = 0xff20,
SHN_HIOS = 0xff3f,
SHN_ABS = 0xfff1,
SHN_COMMON = 0xfff2,
SHN_HIRESERVE = 0xffff
};
enum ElfIdentIndex
{
EI_MAG0 = 0, /* e_ident[] indexes */
EI_MAG1 = 1,
EI_MAG2 = 2,
EI_MAG3 = 3,
EI_CLASS = 4,
EI_DATA = 5,
EI_VERSION = 6,
EI_OSABI = 7,
EI_PAD = 8
};
enum ElfClass
{
ELFCLASSNONE = 0, /* EI_CLASS */
ELFCLASS32 = 1,
ELFCLASS64 = 2,
ELFCLASSNUM = 3
};
enum ElfData
{
ELFDATANONE = 0, /* e_ident[EI_DATA] */
ELFDATA2LSB = 1,
ELFDATA2MSB = 2
};
enum ElfVersion
{
EV_NONE = 0, /* e_version, EI_VERSION */
EV_CURRENT = 1,
EV_NUM = 2,
};
enum ElfOsAbi
{
ELFOSABI_NONE = 0,
ELFOSABI_LINUX =3
};
/*
* Notes used in ET_CORE. Architectures export some of the arch register sets
* using the corresponding note types via the PTRACE_GETREGSET and
* PTRACE_SETREGSET requests.
*/
enum NoteType
{
NT_PRSTATUS = 1,
NT_PRFPREG = 2,
NT_PRPSINFO = 3,
NT_TASKSTRUCT = 4,
NT_AUXV = 6,
/*
* Note to userspace developers: size of NT_SIGINFO note may increase
* in the future to accomodate more fields, don't assume it is fixed!
*/
NT_SIGINFO = 0x53494749,
NT_FILE = 0x46494c45,
NT_PRXFPREG = 0x46e62b7f, /* copied from gdb5.1/include/elf/common.h */
NT_PPC_VMX = 0x100, /* PowerPC Altivec/VMX registers */
NT_PPC_SPE = 0x101, /* PowerPC SPE/EVR registers */
NT_PPC_VSX = 0x102, /* PowerPC VSX registers */
NT_PPC_TAR = 0x103, /* Target Address Register */
NT_PPC_PPR = 0x104, /* Program Priority Register */
NT_PPC_DSCR = 0x105, /* Data Stream Control Register */
NT_PPC_EBB = 0x106, /* Event Based Branch Registers */
NT_PPC_PMU = 0x107, /* Performance Monitor Registers */
NT_PPC_TM_CGPR = 0x108, /* TM checkpointed GPR Registers */
NT_PPC_TM_CFPR = 0x109, /* TM checkpointed FPR Registers */
NT_PPC_TM_CVMX = 0x10a, /* TM checkpointed VMX Registers */
NT_PPC_TM_CVSX = 0x10b, /* TM checkpointed VSX Registers */
NT_PPC_TM_SPR = 0x10c, /* TM Special Purpose Registers */
NT_PPC_TM_CTAR = 0x10d, /* TM checkpointed Target Address Register */
NT_PPC_TM_CPPR = 0x10e, /* TM checkpointed Program Priority Register */
NT_PPC_TM_CDSCR = 0x10f, /* TM checkpointed Data Stream Control Register */
NT_PPC_PKEY = 0x110, /* Memory Protection Keys registers */
NT_386_TLS = 0x200, /* i386 TLS slots (struct user_desc) */
NT_386_IOPERM = 0x201, /* x86 io permission bitmap (1=deny) */
NT_X86_XSTATE = 0x202, /* x86 extended state using xsave */
NT_S390_HIGH_GPRS = 0x300, /* s390 upper register halves */
NT_S390_TIMER = 0x301, /* s390 timer register */
NT_S390_TODCMP = 0x302, /* s390 TOD clock comparator register */
NT_S390_TODPREG = 0x303, /* s390 TOD programmable register */
NT_S390_CTRS = 0x304, /* s390 control registers */
NT_S390_PREFIX = 0x305, /* s390 prefix register */
NT_S390_LAST_BREAK = 0x306, /* s390 breaking event address */
NT_S390_SYSTEM_CALL = 0x307, /* s390 system call restart data */
NT_S390_TDB = 0x308, /* s390 transaction diagnostic block */
NT_S390_VXRS_LOW = 0x309, /* s390 vector registers 0-15 upper half */
NT_S390_VXRS_HIGH = 0x30a, /* s390 vector registers 16-31 */
NT_S390_GS_CB = 0x30b, /* s390 guarded storage registers */
NT_S390_GS_BC = 0x30c, /* s390 guarded storage broadcast control block */
NT_S390_RI_CB = 0x30d, /* s390 runtime instrumentation */
NT_ARM_VFP = 0x400, /* ARM VFP/NEON registers */
NT_ARM_TLS = 0x401, /* ARM TLS register */
NT_ARM_HW_BREAK = 0x402, /* ARM hardware breakpoint registers */
NT_ARM_HW_WATCH = 0x403, /* ARM hardware watchpoint registers */
NT_ARM_SYSTEM_CALL = 0x404, /* ARM system call number */
NT_ARM_SVE = 0x405, /* ARM Scalable Vector Extension registers */
NT_ARC_V2 = 0x600, /* ARCv2 accumulator/extra registers */
NT_VMCOREDD = 0x700, /* Vmcore Device Dump Note */
NT_MIPS_DSP = 0x800, /* MIPS DSP ASE registers */
NT_MIPS_FP_MODE = 0x801, /* MIPS floating-point mode */
};
static const size_t kEIdentSize = 0x10;
static const byte_t kElfMagic[sizeof(uint32_t)] = {0x7f, 'E', 'L', 'F'};
inline byte_t get_elf_st_bind(byte_t st_info) { return st_info >> 4; }
inline byte_t get_elf_st_type(byte_t st_info) { return st_info & 0xf; }
inline byte_t get_elf_st_info(byte_t st_bind, byte_t st_type) { return (st_type & 0xf) | ((st_bind & 0xf) << 4);}
/* The following are used with relocations */
#define ELF32_R_SYM(x) ((x) >> 8)
#define ELF32_R_TYPE(x) ((x) & 0xff)
#define ELF64_R_SYM(i) ((i) >> 32)
#define ELF64_R_TYPE(i) ((i) & 0xffffffff)
}
struct Elf32_Dyn
{
int32_t d_tag;
union{
int32_t d_val;
uint32_t d_ptr;
} d_un;
};
struct Elf64_Dyn
{
int64_t d_tag; /* entry tag value */
union {
uint64_t d_val;
uint64_t d_ptr;
} d_un;
};
struct Elf32_Rel
{
uint32_t r_offset;
uint32_t r_info;
};
struct Elf64_Rel
{
uint64_t r_offset; /* Location at which to apply the action */
uint64_t r_info; /* index and type of relocation */
};
struct Elf32_Rela
{
uint32_t r_offset;
uint32_t r_info;
int32_t r_addend;
};
struct Elf64_Rela
{
uint64_t r_offset; /* Location at which to apply the action */
uint64_t r_info; /* index and type of relocation */
int64_t r_addend; /* Constant addend used to compute value */
};
struct Elf32_Sym
{
uint32_t st_name;
uint32_t st_value;
uint32_t st_size;
byte_t st_info;
byte_t st_other;
uint16_t st_shndx;
};
struct Elf64_Sym
{
uint32_t st_name; /* Symbol name, index in string tbl */
byte_t st_info; /* Type and binding attributes */
byte_t st_other; /* No defined meaning, 0 */
uint16_t st_shndx; /* Associated section index */
uint64_t st_value; /* Value of the symbol */
uint64_t st_size; /* Associated symbol size */
};
struct Elf32_Ehdr
{
byte_t e_ident[elf::kEIdentSize];
uint16_t e_type;
uint16_t e_machine;
uint32_t e_version;
uint32_t e_entry; /* Entry point */
uint32_t e_phoff;
uint32_t e_shoff;
uint32_t e_flags;
uint16_t e_ehsize;
uint16_t e_phentsize;
uint16_t e_phnum;
uint16_t e_shentsize;
uint16_t e_shnum;
uint16_t e_shstrndx;
};
struct Elf64_Ehdr
{
byte_t e_ident[elf::kEIdentSize]; /* ELF "magic number" */
uint16_t e_type;
uint16_t e_machine;
uint32_t e_version;
uint64_t e_entry; /* Entry point virtual address */
uint64_t e_phoff; /* Program header table file offset */
uint64_t e_shoff; /* Section header table file offset */
uint32_t e_flags;
uint16_t e_ehsize;
uint16_t e_phentsize;
uint16_t e_phnum;
uint16_t e_shentsize;
uint16_t e_shnum;
uint16_t e_shstrndx;
};
struct Elf32_Phdr
{
uint32_t p_type;
uint32_t p_offset;
uint32_t p_vaddr;
uint32_t p_paddr;
uint32_t p_filesz;
uint32_t p_memsz;
uint32_t p_flags;
uint32_t p_align;
};
struct Elf64_Phdr
{
uint32_t p_type;
uint32_t p_flags;
uint64_t p_offset; /* Segment file offset */
uint64_t p_vaddr; /* Segment virtual address */
uint64_t p_paddr; /* Segment physical address */
uint64_t p_filesz; /* Segment size in file */
uint64_t p_memsz; /* Segment size in memory */
uint64_t p_align; /* Segment alignment, file & memory */
};
struct Elf32_Shdr
{
uint32_t sh_name;
uint32_t sh_type;
uint32_t sh_flags;
uint32_t sh_addr;
uint32_t sh_offset;
uint32_t sh_size;
uint32_t sh_link;
uint32_t sh_info;
uint32_t sh_addralign;
uint32_t sh_entsize;
};
struct Elf64_Shdr
{
uint32_t sh_name; /* Section name, index in string tbl */
uint32_t sh_type; /* Type of section */
uint64_t sh_flags; /* Miscellaneous section attributes */
uint64_t sh_addr; /* Section virtual addr at execution */
uint64_t sh_offset; /* Section file offset */
uint64_t sh_size; /* Size of section in bytes */
uint32_t sh_link; /* Index of another section */
uint32_t sh_info; /* Additional section information */
uint64_t sh_addralign; /* Section alignment */
uint64_t sh_entsize; /* Entry size if section holds table */
};
/* Note header in a PT_NOTE section */
struct Elf32_Nhdr
{
uint32_t n_namesz; /* Name size */
uint32_t n_descsz; /* Content size */
uint32_t n_type; /* Content type */
};
/* Note header in a PT_NOTE section */
struct Elf64_Nhdr
{
uint32_t n_namesz; /* Name size */
uint32_t n_descsz; /* Content size */
uint32_t n_type; /* Content type */
};
}

View file

@ -1,8 +1,8 @@
#include <cstdio>
#include <fnd/SimpleFile.h>
#include <fnd/SharedPtr.h>
#include <fnd/StringConv.h>
#include "UserSettings.h"
#include <tc.h>
#include <tc/os/UnicodeMain.h>
#include "Settings.h"
#include "GameCardProcess.h"
#include "PfsProcess.h"
#include "RomfsProcess.h"
@ -14,244 +14,213 @@
#include "NacpProcess.h"
#include "IniProcess.h"
#include "KipProcess.h"
#include "PkiCertProcess.h"
#include "EsCertProcess.h"
#include "EsTikProcess.h"
#include "AssetProcess.h"
#ifdef _WIN32
int wmain(int argc, wchar_t** argv)
#else
int main(int argc, char** argv)
#endif
int umain(const std::vector<std::string>& args, const std::vector<std::string>& env)
{
std::vector<std::string> args;
for (size_t i = 0; i < (size_t)argc; i++)
try
{
#ifdef _WIN32
args.push_back(fnd::StringConv::ConvertChar16ToChar8(std::u16string((char16_t*)argv[i])));
#else
args.push_back(argv[i]);
#endif
}
nstool::Settings set = nstool::SettingsInitializer(args);
std::shared_ptr<tc::io::IStream> infile_stream = std::make_shared<tc::io::FileStream>(tc::io::FileStream(set.infile.path.get(), tc::io::FileMode::Open, tc::io::FileAccess::Read));
UserSettings user_set;
try {
user_set.parseCmdArgs(args);
fnd::SharedPtr<fnd::IFile> inputFile(new fnd::SimpleFile(user_set.getInputPath(), fnd::SimpleFile::Read));
if (user_set.getFileType() == FILE_GAMECARD)
if (set.infile.filetype == nstool::Settings::FILE_TYPE_GAMECARD)
{
GameCardProcess obj;
nstool::GameCardProcess obj;
obj.setInputFile(inputFile);
obj.setInputFile(infile_stream);
obj.setKeyCfg(user_set.getKeyCfg());
obj.setCliOutputMode(user_set.getCliOutputMode());
obj.setVerifyMode(user_set.isVerifyFile());
if (user_set.getXciUpdatePath().isSet)
obj.setPartitionForExtract(nn::hac::gc::kUpdatePartitionStr, user_set.getXciUpdatePath().var);
if (user_set.getXciLogoPath().isSet)
obj.setPartitionForExtract(nn::hac::gc::kLogoPartitionStr, user_set.getXciLogoPath().var);
if (user_set.getXciNormalPath().isSet)
obj.setPartitionForExtract(nn::hac::gc::kNormalPartitionStr, user_set.getXciNormalPath().var);
if (user_set.getXciSecurePath().isSet)
obj.setPartitionForExtract(nn::hac::gc::kSecurePartitionStr, user_set.getXciSecurePath().var);
obj.setListFs(user_set.isListFs());
obj.setKeyCfg(set.opt.keybag);
obj.setCliOutputMode(set.opt.cli_output_mode);
obj.setVerifyMode(set.opt.verify);
obj.setShowFsTree(set.fs.show_fs_tree);
obj.setExtractJobs(set.fs.extract_jobs);
obj.process();
}
else if (user_set.getFileType() == FILE_PARTITIONFS || user_set.getFileType() == FILE_NSP)
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_PARTITIONFS || set.infile.filetype == nstool::Settings::FILE_TYPE_NSP)
{
PfsProcess obj;
nstool::PfsProcess obj;
obj.setInputFile(inputFile);
obj.setCliOutputMode(user_set.getCliOutputMode());
obj.setVerifyMode(user_set.isVerifyFile());
obj.setInputFile(infile_stream);
if (user_set.getFsPath().isSet)
obj.setExtractPath(user_set.getFsPath().var);
obj.setListFs(user_set.isListFs());
obj.setCliOutputMode(set.opt.cli_output_mode);
obj.setVerifyMode(set.opt.verify);
obj.setShowFsTree(set.fs.show_fs_tree);
obj.setExtractJobs(set.fs.extract_jobs);
obj.process();
}
else if (user_set.getFileType() == FILE_ROMFS)
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_ROMFS)
{
RomfsProcess obj;
nstool::RomfsProcess obj;
obj.setInputFile(inputFile);
obj.setCliOutputMode(user_set.getCliOutputMode());
obj.setVerifyMode(user_set.isVerifyFile());
obj.setInputFile(infile_stream);
obj.setCliOutputMode(set.opt.cli_output_mode);
obj.setVerifyMode(set.opt.verify);
if (user_set.getFsPath().isSet)
obj.setExtractPath(user_set.getFsPath().var);
obj.setListFs(user_set.isListFs());
obj.setShowFsTree(set.fs.show_fs_tree);
obj.setExtractJobs(set.fs.extract_jobs);
obj.process();
}
else if (user_set.getFileType() == FILE_NCA)
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_NCA)
{
NcaProcess obj;
nstool::NcaProcess obj;
obj.setInputFile(inputFile);
obj.setKeyCfg(user_set.getKeyCfg());
obj.setCliOutputMode(user_set.getCliOutputMode());
obj.setVerifyMode(user_set.isVerifyFile());
obj.setInputFile(infile_stream);
obj.setBaseNcaPath(set.nca.base_nca_path);
obj.setKeyCfg(set.opt.keybag);
obj.setCliOutputMode(set.opt.cli_output_mode);
obj.setVerifyMode(set.opt.verify);
if (user_set.getNcaPart0Path().isSet)
obj.setPartition0ExtractPath(user_set.getNcaPart0Path().var);
if (user_set.getNcaPart1Path().isSet)
obj.setPartition1ExtractPath(user_set.getNcaPart1Path().var);
if (user_set.getNcaPart2Path().isSet)
obj.setPartition2ExtractPath(user_set.getNcaPart2Path().var);
if (user_set.getNcaPart3Path().isSet)
obj.setPartition3ExtractPath(user_set.getNcaPart3Path().var);
obj.setListFs(user_set.isListFs());
obj.setShowFsTree(set.fs.show_fs_tree);
obj.setExtractJobs(set.fs.extract_jobs);
obj.process();
}
else if (user_set.getFileType() == FILE_META)
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_META)
{
MetaProcess obj;
nstool::MetaProcess obj;
obj.setInputFile(inputFile);
obj.setKeyCfg(user_set.getKeyCfg());
obj.setCliOutputMode(user_set.getCliOutputMode());
obj.setVerifyMode(user_set.isVerifyFile());
obj.setInputFile(infile_stream);
obj.setKeyCfg(set.opt.keybag);
obj.setCliOutputMode(set.opt.cli_output_mode);
obj.setVerifyMode(set.opt.verify);
obj.process();
}
else if (user_set.getFileType() == FILE_CNMT)
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_CNMT)
{
CnmtProcess obj;
nstool::CnmtProcess obj;
obj.setInputFile(inputFile);
obj.setCliOutputMode(user_set.getCliOutputMode());
obj.setVerifyMode(user_set.isVerifyFile());
obj.setInputFile(infile_stream);
obj.setCliOutputMode(set.opt.cli_output_mode);
obj.setVerifyMode(set.opt.verify);
obj.process();
}
else if (user_set.getFileType() == FILE_NSO)
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_NSO)
{
NsoProcess obj;
nstool::NsoProcess obj;
obj.setInputFile(inputFile);
obj.setCliOutputMode(user_set.getCliOutputMode());
obj.setVerifyMode(user_set.isVerifyFile());
obj.setInputFile(infile_stream);
obj.setCliOutputMode(set.opt.cli_output_mode);
obj.setVerifyMode(set.opt.verify);
obj.setIs64BitInstruction(user_set.getIs64BitInstruction());
obj.setListApi(user_set.isListApi());
obj.setListSymbols(user_set.isListSymbols());
obj.setIs64BitInstruction(set.code.is_64bit_instruction);
obj.setListApi(set.code.list_api);
obj.setListSymbols(set.code.list_symbols);
obj.process();
}
else if (user_set.getFileType() == FILE_NRO)
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_NRO)
{
NroProcess obj;
nstool::NroProcess obj;
obj.setInputFile(inputFile);
obj.setCliOutputMode(user_set.getCliOutputMode());
obj.setVerifyMode(user_set.isVerifyFile());
obj.setInputFile(infile_stream);
obj.setCliOutputMode(set.opt.cli_output_mode);
obj.setVerifyMode(set.opt.verify);
obj.setIs64BitInstruction(user_set.getIs64BitInstruction());
obj.setListApi(user_set.isListApi());
obj.setListSymbols(user_set.isListSymbols());
obj.setIs64BitInstruction(set.code.is_64bit_instruction);
obj.setListApi(set.code.list_api);
obj.setListSymbols(set.code.list_symbols);
if (user_set.getAssetIconPath().isSet)
obj.setAssetIconExtractPath(user_set.getAssetIconPath().var);
if (user_set.getAssetNacpPath().isSet)
obj.setAssetNacpExtractPath(user_set.getAssetNacpPath().var);
if (set.aset.icon_extract_path.isSet())
obj.setAssetIconExtractPath(set.aset.icon_extract_path.get());
if (set.aset.nacp_extract_path.isSet())
obj.setAssetNacpExtractPath(set.aset.nacp_extract_path.get());
if (user_set.getFsPath().isSet)
obj.setAssetRomfsExtractPath(user_set.getFsPath().var);
obj.setAssetListFs(user_set.isListFs());
obj.setAssetRomfsShowFsTree(set.fs.show_fs_tree);
obj.setAssetRomfsExtractJobs(set.fs.extract_jobs);
obj.process();
}
else if (user_set.getFileType() == FILE_NACP)
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_NACP)
{
NacpProcess obj;
nstool::NacpProcess obj;
obj.setInputFile(inputFile);
obj.setCliOutputMode(user_set.getCliOutputMode());
obj.setVerifyMode(user_set.isVerifyFile());
obj.setInputFile(infile_stream);
obj.setCliOutputMode(set.opt.cli_output_mode);
obj.setVerifyMode(set.opt.verify);
obj.process();
}
else if (user_set.getFileType() == FILE_INI)
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_INI)
{
IniProcess obj;
nstool::IniProcess obj;
obj.setInputFile(inputFile);
obj.setCliOutputMode(user_set.getCliOutputMode());
obj.setVerifyMode(user_set.isVerifyFile());
obj.setInputFile(infile_stream);
obj.setCliOutputMode(set.opt.cli_output_mode);
obj.setVerifyMode(set.opt.verify);
if (user_set.getKipExtractPath().isSet)
obj.setKipExtractPath(user_set.getKipExtractPath().var);
if (set.kip.extract_path.isSet())
obj.setKipExtractPath(set.kip.extract_path.get());
obj.process();
}
else if (user_set.getFileType() == FILE_KIP)
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_KIP)
{
KipProcess obj;
nstool::KipProcess obj;
obj.setInputFile(inputFile);
obj.setCliOutputMode(user_set.getCliOutputMode());
obj.setVerifyMode(user_set.isVerifyFile());
obj.setInputFile(infile_stream);
obj.setCliOutputMode(set.opt.cli_output_mode);
obj.setVerifyMode(set.opt.verify);
obj.process();
}
else if (user_set.getFileType() == FILE_PKI_CERT)
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_ES_CERT)
{
PkiCertProcess obj;
nstool::EsCertProcess obj;
obj.setInputFile(inputFile);
obj.setKeyCfg(user_set.getKeyCfg());
obj.setCliOutputMode(user_set.getCliOutputMode());
obj.setVerifyMode(user_set.isVerifyFile());
obj.setInputFile(infile_stream);
obj.setKeyCfg(set.opt.keybag);
obj.setCliOutputMode(set.opt.cli_output_mode);
obj.setVerifyMode(set.opt.verify);
obj.process();
}
else if (user_set.getFileType() == FILE_ES_TIK)
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_ES_TIK)
{
EsTikProcess obj;
nstool::EsTikProcess obj;
obj.setInputFile(inputFile);
obj.setKeyCfg(user_set.getKeyCfg());
obj.setCertificateChain(user_set.getCertificateChain());
obj.setCliOutputMode(user_set.getCliOutputMode());
obj.setVerifyMode(user_set.isVerifyFile());
obj.setInputFile(infile_stream);
obj.setKeyCfg(set.opt.keybag);
//obj.setCertificateChain(user_set.getCertificateChain());
obj.setCliOutputMode(set.opt.cli_output_mode);
obj.setVerifyMode(set.opt.verify);
obj.process();
}
else if (user_set.getFileType() == FILE_HB_ASSET)
else if (set.infile.filetype == nstool::Settings::FILE_TYPE_HB_ASSET)
{
AssetProcess obj;
nstool::AssetProcess obj;
obj.setInputFile(inputFile);
obj.setCliOutputMode(user_set.getCliOutputMode());
obj.setVerifyMode(user_set.isVerifyFile());
obj.setInputFile(infile_stream);
obj.setCliOutputMode(set.opt.cli_output_mode);
obj.setVerifyMode(set.opt.verify);
if (user_set.getAssetIconPath().isSet)
obj.setIconExtractPath(user_set.getAssetIconPath().var);
if (user_set.getAssetNacpPath().isSet)
obj.setNacpExtractPath(user_set.getAssetNacpPath().var);
if (set.aset.icon_extract_path.isSet())
obj.setIconExtractPath(set.aset.icon_extract_path.get());
if (set.aset.nacp_extract_path.isSet())
obj.setNacpExtractPath(set.aset.nacp_extract_path.get());
if (user_set.getFsPath().isSet)
obj.setRomfsExtractPath(user_set.getFsPath().var);
obj.setListFs(user_set.isListFs());
obj.setRomfsShowFsTree(set.fs.show_fs_tree);
obj.setRomfsExtractJobs(set.fs.extract_jobs);
obj.process();
}
else
{
throw fnd::Exception("main", "Unhandled file type");
}
}
catch (const fnd::Exception& e) {
printf("\n\n%s\n", e.what());
catch (tc::Exception& e)
{
fmt::print("[{0}{1}ERROR] {2}\n", e.module(), (strlen(e.module()) != 0 ? " ": ""), e.error());
return 1;
}
return 0;
}

32
src/types.h Normal file
View file

@ -0,0 +1,32 @@
#pragma once
#include <tc/types.h>
#include <tc/Exception.h>
#include <tc/Optional.h>
#include <tc/io.h>
#include <tc/io/IOUtil.h>
#include <tc/cli.h>
#include <fmt/core.h>
namespace nstool {
struct CliOutputMode
{
bool show_basic_info;
bool show_extended_info;
bool show_layout;
bool show_keydata;
CliOutputMode() : show_basic_info(false), show_extended_info(false), show_layout(false), show_keydata(false)
{}
CliOutputMode(bool show_basic_info, bool show_extended_info, bool show_layout, bool show_keydata) : show_basic_info(show_basic_info), show_extended_info(show_extended_info), show_layout(show_layout), show_keydata(show_keydata)
{}
};
struct ExtractJob {
tc::io::Path virtual_path;
tc::io::Path extract_path;
};
}

150
src/util.cpp Normal file
View file

@ -0,0 +1,150 @@
#include "util.h"
#include <tc/io/FileStream.h>
#include <tc/io/SubStream.h>
#include <tc/io/IOUtil.h>
#include <sstream>
#include <algorithm>
#include <iostream>
inline bool isNotPrintable(char chr) { return isprint(chr) == false; }
void nstool::processResFile(const std::shared_ptr<tc::io::IStream>& file, std::map<std::string, std::string>& dict)
{
if (file == nullptr || !file->canRead() || file->length() == 0)
{
return;
}
std::stringstream in_stream;
// populate string stream
tc::ByteData cache = tc::ByteData(0x1000);
file->seek(0, tc::io::SeekOrigin::Begin);
for (int64_t pos = 0; pos < file->length();)
{
size_t bytes_read = file->read(cache.data(), cache.size());
in_stream << std::string((char*)cache.data(), bytes_read);
pos += tc::io::IOUtil::castSizeToInt64(bytes_read);
}
// process stream
std::string line, key, value;
while (std::getline(in_stream, line))
{
// read up to comment line
if (line.find(";") != std::string::npos)
line = line.substr(0, line.find(";"));
// change chars to lower string
std::transform(line.begin(), line.end(), line.begin(), ::tolower);
// strip whitespace
line.erase(std::remove(line.begin(), line.end(), ' '), line.end());
// strip nonprintable
line.erase(std::remove_if(line.begin(), line.end(), isNotPrintable), line.end());
// skip lines that don't have '='
if (line.find("=") == std::string::npos)
continue;
key = line.substr(0,line.find("="));
value = line.substr(line.find("=")+1);
// skip if key or value is empty
if (key.empty() || value.empty())
continue;
//std::cout << "[" + key + "]=(" + value + ")" << std::endl;
dict[key] = value;
}
}
void nstool::writeSubStreamToFile(const std::shared_ptr<tc::io::IStream>& in_stream, int64_t offset, int64_t length, const tc::io::Path& out_path, tc::ByteData& cache)
{
writeStreamToStream(std::make_shared<tc::io::SubStream>(tc::io::SubStream(in_stream, offset, length)), std::make_shared<tc::io::FileStream>(tc::io::FileStream(out_path, tc::io::FileMode::Create, tc::io::FileAccess::Write)), cache);
}
void nstool::writeSubStreamToFile(const std::shared_ptr<tc::io::IStream>& in_stream, int64_t offset, int64_t length, const tc::io::Path& out_path, size_t cache_size)
{
writeStreamToStream(std::make_shared<tc::io::SubStream>(tc::io::SubStream(in_stream, offset, length)), std::make_shared<tc::io::FileStream>(tc::io::FileStream(out_path, tc::io::FileMode::Create, tc::io::FileAccess::Write)), cache_size);
}
void nstool::writeStreamToFile(const std::shared_ptr<tc::io::IStream>& in_stream, const tc::io::Path& out_path, tc::ByteData& cache)
{
writeStreamToStream(in_stream, std::make_shared<tc::io::FileStream>(tc::io::FileStream(out_path, tc::io::FileMode::Create, tc::io::FileAccess::Write)), cache);
}
void nstool::writeStreamToFile(const std::shared_ptr<tc::io::IStream>& in_stream, const tc::io::Path& out_path, size_t cache_size)
{
writeStreamToStream(in_stream, std::make_shared<tc::io::FileStream>(tc::io::FileStream(out_path, tc::io::FileMode::Create, tc::io::FileAccess::Write)), cache_size);
}
void nstool::writeStreamToStream(const std::shared_ptr<tc::io::IStream>& in_stream, const std::shared_ptr<tc::io::IStream>& out_stream, tc::ByteData& cache)
{
// iterate thru child files
size_t cache_read_len;
in_stream->seek(0, tc::io::SeekOrigin::Begin);
out_stream->seek(0, tc::io::SeekOrigin::Begin);
for (int64_t remaining_data = in_stream->length(); remaining_data > 0;)
{
cache_read_len = in_stream->read(cache.data(), cache.size());
if (cache_read_len == 0)
{
throw tc::io::IOException("nstool::writeStreamToStream()", "Failed to read from source streeam.");
}
out_stream->write(cache.data(), cache_read_len);
remaining_data -= int64_t(cache_read_len);
}
}
void nstool::writeStreamToStream(const std::shared_ptr<tc::io::IStream>& in_stream, const std::shared_ptr<tc::io::IStream>& out_stream, size_t cache_size)
{
tc::ByteData cache = tc::ByteData(cache_size);
writeStreamToStream(in_stream, out_stream, cache);
}
std::string nstool::getTruncatedBytesString(const byte_t* data, size_t len)
{
if (data == nullptr) { return fmt::format(""); }
std::string str = "";
if (len <= 8)
{
str = tc::cli::FormatUtil::formatBytesAsString(data, len, true, "");
}
else
{
str = fmt::format("{:02X}{:02X}{:02X}{:02X}...{:02X}{:02X}{:02X}{:02X}", data[0], data[1], data[2], data[3], data[len-4], data[len-3], data[len-2], data[len-1]);
}
return str;
}
std::string nstool::getTruncatedBytesString(const byte_t* data, size_t len, bool do_not_truncate)
{
if (data == nullptr) { return fmt::format(""); }
std::string str = "";
if (len <= 8 || do_not_truncate)
{
str = tc::cli::FormatUtil::formatBytesAsString(data, len, true, "");
}
else
{
str = fmt::format("{:02X}{:02X}{:02X}{:02X}...{:02X}{:02X}{:02X}{:02X}", data[0], data[1], data[2], data[3], data[len-4], data[len-3], data[len-2], data[len-1]);
}
return str;
}

20
src/util.h Normal file
View file

@ -0,0 +1,20 @@
#pragma once
#include "types.h"
namespace nstool
{
void processResFile(const std::shared_ptr<tc::io::IStream>& file, std::map<std::string, std::string>& dict);
void writeSubStreamToFile(const std::shared_ptr<tc::io::IStream>& in_stream, int64_t offset, int64_t length, const tc::io::Path& out_path, tc::ByteData& cache);
void writeSubStreamToFile(const std::shared_ptr<tc::io::IStream>& in_stream, int64_t offset, int64_t length, const tc::io::Path& out_path, size_t cache_size = 0x10000);
void writeStreamToFile(const std::shared_ptr<tc::io::IStream>& in_stream, const tc::io::Path& out_path, tc::ByteData& cache);
void writeStreamToFile(const std::shared_ptr<tc::io::IStream>& in_stream, const tc::io::Path& out_path, size_t cache_size = 0x10000);
void writeStreamToStream(const std::shared_ptr<tc::io::IStream>& in_stream, const std::shared_ptr<tc::io::IStream>& out_stream, tc::ByteData& cache);
void writeStreamToStream(const std::shared_ptr<tc::io::IStream>& in_stream, const std::shared_ptr<tc::io::IStream>& out_stream, size_t cache_size = 0x10000);
std::string getTruncatedBytesString(const byte_t* data, size_t len);
std::string getTruncatedBytesString(const byte_t* data, size_t len, bool do_not_truncate);
}

View file

@ -1,7 +1,7 @@
#pragma once
#define APP_NAME "NSTool"
#define BIN_NAME "nstool"
#define VER_MAJOR 1
#define VER_MINOR 4
#define VER_PATCH 1
#define VER_MAJOR 0
#define VER_MINOR 0
#define VER_PATCH 0
#define AUTHORS "jakcron"