Compare commits

...

No commits in common. "v1.1.8" and "rewrite" have entirely different histories.

209 changed files with 51202 additions and 25874 deletions

View File

@ -2,7 +2,7 @@
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
labels: bug
assignees: ''
---
@ -17,18 +17,21 @@ Steps to reproduce the behavior:
3. Scroll down to '....'
4. See error
**Please fill the following information:**
- Horizon OS (Switch FW) version: [e.g. 10.0.0]
- CFW: [e.g. Atmosphère, SX OS, etc.]
- CFW version: [e.g. 0.11.1, 2.9.4, etc.]
- Atmosphère launch method (if applicable): [e.g. Hekate, fusee-primary]
- nxdumptool version: [e.g. 2.0.0]
- Homebrew launch method: [e.g. title override, applet]
- Source storage used with the application (if applicable): [e.g. gamecard, SD/eMMC]
- SD card specs: [e.g. Samsung EVO 256 GB, FAT32 partition]
**Screenshots**
Add screenshots to help explain your problem.
**Please complete the following information:**
- Horizon OS (Switch FW) version: [e.g. 9.0.1]
- CFW: [e.g. Atmosphère, SX OS, etc.]
- CFW version: [e.g. 0.9.4, 2.9.2, etc.]
- Atmosphère launch method (if applicable): [e.g. Hekate, fusee-primary]
- NXDumpTool version: [e.g. 1.1.7]
- Homebrew launch method: [e.g. title override, Album applet]
- Source storage used with the application (if applicable): [e.g. gamecard, SD/eMMC]
- SD card specs: [e.g. Samsung EVO 256 GB]
**Logfile**
If available, please upload your logfile located at `sdmc:/nxdumptool/nxdumptool.log`.
**Additional context**
Add any other context about the problem here.

View File

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

116
.github/workflows/rewrite.yml vendored Normal file
View File

@ -0,0 +1,116 @@
name: Build nxdumptool-rewrite binary
on:
push:
branches: [ rewrite ]
paths:
- '.github/workflows/rewrite.yml'
- 'code_templates/**'
- 'include/**'
- 'libs/**'
- 'romfs/**'
- 'source/**'
- 'build.sh'
- 'Makefile'
# Allows you to run this workflow manually from the Actions tab.
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
container:
image: devkitpro/devkita64
defaults:
run:
shell: bash
steps:
- name: Set environment variables
run: |
echo "nxdt_commit=${GITHUB_SHA::7}" >> $GITHUB_ENV
- name: Clone nxdumptool repository
uses: actions/checkout@v4
with:
path: nxdumptool
submodules: recursive
- name: Build nxdumptool-rewrite PoC binary
id: build
continue-on-error: true
working-directory: nxdumptool
run: |
chmod +x ./build.sh
./build.sh --noconfirm
- name: Clone libnx repository
if: ${{ steps.build.outcome == 'failure' }}
uses: actions/checkout@v4
with:
repository: switchbrew/libnx
path: libnx
submodules: recursive
- name: Install latest libnx commit and retry build
if: ${{ steps.build.outcome == 'failure' }}
run: |
cd "$GITHUB_WORKSPACE/libnx"
make install -j8
cd "$GITHUB_WORKSPACE/nxdumptool"
./build.sh --noconfirm
- name: Build nxdumptool-rewrite UI binary
working-directory: nxdumptool
run: |
make -j8
- name: Upload nxdumptool-rewrite PoC NRO artifact
uses: actions/upload-artifact@v4
with:
name: nxdt_rw_poc-${{ env.nxdt_commit }}.nro
path: nxdumptool/code_templates/tmp/nxdt_rw_poc.nro
if-no-files-found: error
- name: Upload nxdumptool-rewrite PoC ELF artifact
uses: actions/upload-artifact@v4
with:
name: nxdt_rw_poc-${{ env.nxdt_commit }}.elf
path: nxdumptool/code_templates/tmp/nxdt_rw_poc.elf
if-no-files-found: error
- name: Upload nxdumptool-rewrite UI NRO artifact
uses: actions/upload-artifact@v4
with:
name: nxdumptool-rewrite-${{ env.nxdt_commit }}-WIP_UI.nro
path: nxdumptool/nxdumptool.nro
if-no-files-found: error
- name: Upload nxdumptool-rewrite UI ELF artifact
uses: actions/upload-artifact@v4
with:
name: nxdumptool-rewrite-${{ env.nxdt_commit }}-WIP_UI.elf
path: nxdumptool/nxdumptool.elf
if-no-files-found: error
- name: Upload nxdumptool-rewrite PoC artifacts to pre-release
uses: ncipollo/release-action@v1
with:
# Only update attachments on "rewrite-prerelease" tag.
prerelease: True
tag: "rewrite-prerelease"
updateOnlyUnreleased: True
# Remove old artifacts and replace with new ones.
removeArtifacts: True
replacesArtifacts: True
# Update preferences.
allowUpdates: True
omitBody: True
omitBodyDuringUpdate: True
omitNameDuringUpdate: True
artifacts: "nxdumptool/code_templates/tmp/nxdt_rw_poc.*"
#artifacts: "nxdumptool/code_templates/tmp/nxdt_rw_poc.*, nxdumptool/nxdumptool.*"
token: ${{ secrets.GITHUB_TOKEN }}

31
.gitignore vendored
View File

@ -1,13 +1,22 @@
.vscode
build
/*.elf
/*.nacp
/*.nro
/*.nso
/*.map
/*.pfs0
/*.lst
host/__pycache__
host/standalone
host/nxdt_host*
host/nxdumptool
*.elf
*.nacp
*.nro
*.nso
*.map
*.pfs0
*.lst
*.log
*.spec
*.exe
*.7z
*.code-workspace
# Clion files
.idea
cmake-build-debug
CMakeLists.txt
# TODO: remove this after the PoC builds are no longer needed.
main.cpp
/code_templates/tmp/*

8
.gitmodules vendored Normal file
View File

@ -0,0 +1,8 @@
[submodule "libusbhsfs"]
path = libs/libusbhsfs
url = https://github.com/DarkMatterCore/libusbhsfs
branch = dev
[submodule "borealis"]
path = libs/borealis
url = https://github.com/DarkMatterCore/borealis
branch = nxdumptool-legacy

View File

@ -1,124 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
The precise terms and conditions for copying, distribution and modification follow.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
The precise terms and conditions for copying, distribution and
modification follow.
0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you".
TERMS AND CONDITIONS
Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.
0. Definitions.
1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.
"This License" refers to version 3 of the GNU General Public License.
You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.
A "covered work" means either the unmodified Program or a work based
on the Program.
c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.)
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.
1. Source Code.
3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following:
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.)
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.
The Corresponding Source for a work in source code form is that
same work.
4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
2. Basic Permissions.
5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.
4. Conveying Verbatim Copies.
9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.
5. Conveying Modified Source Versions.
NO WARRANTY
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
END OF TERMS AND CONDITIONS
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
How to Apply These Terms to Your New Programs
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.
6. Conveying Non-Source Forms.
One line to give the program's name and a brief idea of what it does.
Copyright (C) <year> <name of author>
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this when it starts in an interactive mode:
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details.
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names:
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker.
signature of Ty Coon, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.

144
Makefile
View File

@ -6,6 +6,9 @@ ifeq ($(strip $(DEVKITPRO)),)
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
endif
# Uncomment to enable verbose output.
#V:=1
TOPDIR ?= $(CURDIR)
include $(DEVKITPRO)/libnx/switch_rules
@ -15,7 +18,6 @@ include $(DEVKITPRO)/libnx/switch_rules
# SOURCES is a list of directories containing source code
# DATA is a list of directories containing data files
# INCLUDES is a list of directories containing header files
# EXEFS_SRC is the optional input directory containing data copied into exefs, if anything this normally should only contain "main.npdm".
# ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional)
#
# NO_ICON: if set to anything, do not use icon.
@ -29,52 +31,88 @@ include $(DEVKITPRO)/libnx/switch_rules
# - <Project name>.jpg
# - icon.jpg
# - <libnx folder>/default_icon.jpg
#
# CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder.
# If not set, it attempts to use one of the following (in this order):
# - <Project name>.json
# - config.json
# If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead
# of a homebrew executable (.nro). This is intended to be used for sysmodules.
# NACP building is skipped as well.
#---------------------------------------------------------------------------------
VERSION_MAJOR := 1
VERSION_MINOR := 1
VERSION_MICRO := 8
ROOTDIR ?= $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
APP_TITLE := nxdumptool
APP_AUTHOR := MCMrARM, DarkMatterCore
APP_VERSION := ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_MICRO}
GIT_BRANCH := $(strip $(shell git rev-parse --abbrev-ref HEAD 2>/dev/null))
GIT_COMMIT := $(strip $(shell git rev-parse --short HEAD 2>/dev/null))
GIT_REV := ${GIT_BRANCH}-${GIT_COMMIT}
TARGET := nxdumptool
BUILD := build
SOURCES := source source/fatfs
DATA := data
INCLUDES := include
EXEFS_SRC := exefs_src
ROMFS := romfs
ifneq (,$(strip $(shell git status --porcelain 2>/dev/null)))
GIT_REV := $(GIT_REV)-dirty
endif
ifeq (,$(GIT_BRANCH))
$(error GIT_BRANCH is empty)
endif
ifeq (,$(GIT_COMMIT))
$(error GIT_COMMIT is empty)
endif
VERSION_MAJOR := 2
VERSION_MINOR := 0
VERSION_MICRO := 0
APP_TITLE := nxdumptool
APP_AUTHOR := DarkMatterCore
APP_VERSION := ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_MICRO}
# TODO: remove this after the PoC builds are no longer needed.
ifneq ($(origin BUILD_TYPE),undefined)
APP_TITLE := ${BUILD_TYPE}
endif
BUILD_TIMESTAMP := $(strip $(shell date --utc '+%Y-%m-%d %T UTC'))
TARGET := ${APP_TITLE}
BUILD := build
SOURCES := source source/core source/core/fatfs source/core/devoptab source/tasks source/utils source/views
DATA := data
ICON := romfs/icon/${APP_TITLE}.jpg
INCLUDES := include
ROMFS := romfs
BOREALIS_PATH := libs/borealis
BOREALIS_RESOURCES := romfs:/
USBHSFS_PATH := $(ROOTDIR)/libs/libusbhsfs
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE
CFLAGS := -g -Wall -Wno-address-of-packed-member -O2 -ffunction-sections \
$(ARCH) $(DEFINES)
CFLAGS := -g -Wall -Werror -O2 -ffunction-sections $(ARCH) $(DEFINES) $(INCLUDE) -D__SWITCH__
CFLAGS += -DVERSION_MAJOR=${VERSION_MAJOR} -DVERSION_MINOR=${VERSION_MINOR} -DVERSION_MICRO=${VERSION_MICRO}
CFLAGS += -DAPP_TITLE="\"${APP_TITLE}\"" -DAPP_AUTHOR="\"${APP_AUTHOR}\"" -DAPP_VERSION="\"${APP_VERSION}\""
CFLAGS += -DGIT_BRANCH="\"${GIT_BRANCH}\"" -DGIT_COMMIT="\"${GIT_COMMIT}\"" -DGIT_REV="\"${GIT_REV}\""
CFLAGS += -DBUILD_TIMESTAMP="\"${BUILD_TIMESTAMP}\"" -DBOREALIS_RESOURCES="\"${BOREALIS_RESOURCES}\"" -D_GNU_SOURCE
CFLAGS += -fmacro-prefix-map=$(ROOTDIR)=
CFLAGS += $(INCLUDE) -D__SWITCH__ -D__LINUX_ERRNO_EXTENSIONS__ -DAPP_VERSION=\"${APP_VERSION}\"
CFLAGS += `freetype-config --cflags`
CFLAGS += `aarch64-none-elf-pkg-config zlib --cflags`
CFLAGS += `aarch64-none-elf-pkg-config libxml-2.0 --cflags`
CFLAGS += `aarch64-none-elf-pkg-config json-c --cflags`
CFLAGS += `aarch64-none-elf-pkg-config libturbojpeg --cflags`
CXXFLAGS := $(CFLAGS) -std=c++20
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
ASFLAGS := -g $(ARCH)
LDFLAGS := -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
LIBS := -lcurl -lmbedtls -lmbedx509 -lmbedcrypto -lxml2 -lz -lnx -ljson-c -lm `freetype-config --libs` -lturbojpeg
LIBS := -lcurl -lmbedtls -lmbedx509 -lmbedcrypto -lxml2 -ljson-c -lz -lusbhsfs -lntfs-3g -llwext4 -lnx
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS := $(PORTLIBS) $(LIBNX)
LIBDIRS := $(PORTLIBS) $(LIBNX) $(USBHSFS_PATH)
include $(ROOTDIR)/$(BOREALIS_PATH)/library/borealis.mk
#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
@ -121,7 +159,18 @@ export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC)
ifeq ($(strip $(CONFIG_JSON)),)
jsons := $(wildcard *.json)
ifneq (,$(findstring $(TARGET).json,$(jsons)))
export APP_JSON := $(TOPDIR)/$(TARGET).json
else
ifneq (,$(findstring config.json,$(jsons)))
export APP_JSON := $(TOPDIR)/config.json
endif
endif
else
export APP_JSON := $(TOPDIR)/$(CONFIG_JSON)
endif
ifeq ($(strip $(ICON)),)
icons := $(wildcard *.jpg)
@ -152,20 +201,29 @@ ifneq ($(ROMFS),)
export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS)
endif
.PHONY: $(BUILD) clean all
.PHONY: $(BUILD) all clean clean_all
#---------------------------------------------------------------------------------
all: $(BUILD)
$(BUILD):
$(BUILD): usbhsfs
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
@MSYS2_ARG_CONV_EXCL="-D;$(MSYS2_ARG_CONV_EXCL)" $(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
usbhsfs:
@$(MAKE) --no-print-directory -C $(USBHSFS_PATH) BUILD_TYPE=GPL release
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) $(TARGET).pfs0 $(TARGET).nso $(TARGET).nro $(TARGET).nacp $(TARGET).elf
ifeq ($(strip $(APP_JSON)),)
@rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf
else
@rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf
endif
clean_all: clean
@$(MAKE) --no-print-directory -C $(USBHSFS_PATH) clean
#---------------------------------------------------------------------------------
else
@ -176,11 +234,9 @@ DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
all : $(OUTPUT).pfs0 $(OUTPUT).nro
ifeq ($(strip $(APP_JSON)),)
$(OUTPUT).pfs0 : $(OUTPUT).nso
$(OUTPUT).nso : $(OUTPUT).elf
all : $(OUTPUT).nro
ifeq ($(strip $(NO_NACP)),)
$(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp
@ -188,6 +244,16 @@ else
$(OUTPUT).nro : $(OUTPUT).elf
endif
else
all : $(OUTPUT).nsp
$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm
$(OUTPUT).nso : $(OUTPUT).elf
endif
$(OUTPUT).elf : $(OFILES)
$(OFILES_SRC) : $(HFILES_BIN)
@ -195,7 +261,7 @@ $(OFILES_SRC) : $(HFILES_BIN)
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o : %.bin
%.bin.o %_bin.h : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)

483
README.md
View File

@ -1,455 +1,48 @@
# nxdumptool
# nxdumptool
<img width="200" src="romfs/icon/nxdumptool.jpg">
Nintendo Switch Dump Tool
Main features
--------------
### Official Discord server: https://discord.gg/SCbbcQx
* Generates full Cartridge Image dumps (XCI) with optional certificate removal and/or trimming.
* Generates installable Nintendo Submission Packages (NSP) from base applications, updates and DLCs stored in the inserted gamecard, SD card and eMMC storage devices.
* The generated dumps follow the `AuditingTool` format from Scene releases.
* Capable of generating dumps without console specific information (common ticket).
* Capable of generating ticket-less (standard crypto) dumps.
* Capable of generating dumps from installed updates/DLCs with missing base applications (orphan titles).
* Compatible with game pre-installs.
* Batch mode available, with customizable dump settings.
* Manual gamecard certificate dump.
* Manual ticket dump from installed SD/eMMC titles + optional removal of console specific data.
* Compatible with multigame carts.
* CRC32 checksum calculation for XCI/NSP dumps.
* XCI/NSP dump verification through CRC32 checksum lookup.
* Using offline XML database from NSWDB.COM (NSWreleases.xml) (XCI only).
* Performing an online lookup against the No-Intro database.
* Bundled-in update capabilities via libcurl.
* Update to the latest version by downloading it right from GitHub.
* Update the NSWDB.COM XML database.
* Precise HFS0 raw partition dumping, using the root HFS0 header from the gamecard.
* HFS0 partition file data dumping + browser with manual file dump support.
* Program NCA ExeFS/RomFS section & Data NCA RomFS section file data dumping + browser with manual file dump support.
* Compatible with base applications, updates and DLCs (if available).
* Supports manual RomFS directory dumping.
* Free SD card space checks in place.
* File splitting support for all operations.
* Capable of storing split XCI/NSP dumps in directories with the archive bit set.
* Sequential (multi-session) dump support, in case there's not enough storage space available for a XCI/NSP full dump.
* Metadata retrieval using NCM and NS services.
* Dump speed calculation, ETA calculation and progress bar.
This branch is used for the development of the ongoing nxdumptool rewrite. Code is highly experimental and lacks a proper UI at this time, but it has been thoroughly tested and should be safe to use. Proof-of-concept (PoC) builds are provided to test different aspects of the codebase.
Operations related to installed SD/eMMC titles require a keys file located at "sdmc:/switch/prod.keys". Use [Lockpick_RCM](https://github.com/shchmue/Lockpick_RCM) to generate it.
If a nxdumptool build from the [releases page](https://github.com/DarkMatterCore/nxdumptool/releases) isn't working for whatever you're trying to do (e.g. RomFS dumping), odds are it will work with a PoC build from this particular branch. The [old branch](https://github.com/DarkMatterCore/nxdumptool/tree/main) will not receive any further updates.
Please launch the application through title override (hold R while launching a game) whenever possible to avoid memory allocation problems.
A [GitHub workflow](https://github.com/DarkMatterCore/nxdumptool/actions) is used to automatically build each new commit -- feel free to check it out. Furthermore, the assets from the [rewrite-prerelease](https://github.com/DarkMatterCore/nxdumptool/releases/tag/rewrite-prerelease) tag are automatically updated by this workflow, so you can always find the latest NRO binary there.
Thanks to
--------------
This readme will be updated whenever the rewrite branch is ready for a proper release.
* [MCMrARM](https://github.com/MCMrARM), for creating the original application.
* [RSDuck](https://github.com/RSDuck), for vba-next-switch port. It's UI menu code was taken as a basis for this application.
* [foen](https://github.com/foen), for giving me some pretty good hints about how to use the NCM service.
* [yellows8](https://github.com/yellows8), for helping me fix a silly bug in my implementation of some NCM service IPC calls.
* [SciresM](https://github.com/SciresM), for [hactool](https://github.com/SciresM/hactool) (licensed under [ISC](https://github.com/SciresM/hactool/blob/master/LICENSE)). It's NCA content handling procedures are reproduced in many parts of the application.
* [The-4n](https://github.com/The-4n), for [4NXCI](https://github.com/The-4n/4NXCI) (licensed under [ISC](https://github.com/The-4n/4NXCI/blob/master/LICENSE)) and [hacPack](https://github.com/The-4n/hacPack) (licensed under [GPLv2](https://github.com/The-4n/hacPack/blob/master/LICENSE)). The NCA content patching procedure used in 4NXCI is replicated in the application, as well as the NACP XML generation from hacPack.
* [shchmue](https://github.com/shchmue), for [Lockpick](https://github.com/shchmue/Lockpick) (licensed under [GPLv2](https://github.com/shchmue/Lockpick/blob/master/LICENSE)). It is used as a reference for the runtime key-collection algorithm needed for the NSP dump, ExeFS dump/browse and RomFS dump/browse procedures.
* Björn Samuelsson, for his [public domain CRC32 checksum calculation C-code](http://home.thep.lu.se/~bjorn/crc).
* [Adubbz](https://github.com/Adubbz), for [Tinfoil](https://github.com/Adubbz/Tinfoil) (licensed under [MIT](https://github.com/Adubbz/Tinfoil/blob/master/LICENSE)). Its wrappers for ES service IPC calls are used in the application.
* [WerWolv](https://github.com/WerWolv), for the SX OS detection procedure used in [EdiZon](https://github.com/WerWolv/EdiZon) (licensed under [GPLv2](https://github.com/WerWolv/EdiZon/blob/master/LICENSE)).
* ChaN, for the [FatFs module](http://elm-chan.org/fsw/ff/00index_e.html) (licensed under [FatFs license](http://elm-chan.org/fsw/ff/doc/appnote.html#license)). It is used to read ES savedata files from the BIS System partition.
* [hthh](https://github.com/hthh), for his [switch-reversing](https://github.com/hthh/switch-reversing) repository, which was very helpful to understand how to parse and read information from NSO binaries.
* The [LZ4 project](http://www.lz4.org), for the LZ4 C-code implementation (licensed under [BSD 2-Clause](https://github.com/lz4/lz4/blob/master/lib/LICENSE)).
* [AnalogMan](https://github.com/AnalogMan151) and [0Liam](https://github.com/0Liam), for their constant support and ideas.
* [RattletraPM](https://github.com/RattletraPM), for the awesome icon used in the application.
* [FennecTECH](https://github.com/fennectech), for testing / breaking stuff inside the application on a regular basis.
* The folks from NSWDB.COM and No-Intro.org, for being kind enough to put up a HTTP(S) endpoint(s) to perform CRC32 checksum lookups.
* The GNOME project, from which the [high contrast icons](https://commons.wikimedia.org/wiki/GNOME_High_contrast_icons) were retrieved.
* The folks from ReSwitched, for working towards the creation of a good homebrew ecosystem.
* The Comfy Boyes, for being both awesome and supportive. You know who you are.
* My girlfriend, for putting up with me even though I dedicate most of my free time to this little project, and for doing her best to cheer me up and keep me going. I love you.
Currently planned changes for this branch include:
Donate
--------------
* [USB ABI](https://github.com/DarkMatterCore/nxdumptool/tree/rewrite/host) (dump data directly to a PC). :white_check_mark:
* USB Mass Storage device support (FAT, NTFS, EXT) via [libusbhsfs](https://github.com/DarkMatterCore/libusbhsfs). :white_check_mark:
* [NX Card Image (XCI)](https://switchbrew.org/wiki/XCI) gamecard dumps, with optional KeyArea prepending and certificate removal. :white_check_mark:
* [Gamecard header](https://switchbrew.org/wiki/XCI#CardHeader) dumps. :white_check_mark:
* [Gamecard certificate](https://switchbrew.org/wiki/XCI#CertArea) dumps. :white_check_mark:
* Plaintext [gamecard CardInfo area](https://switchbrew.org/wiki/XCI#CardHeaderEncryptedData) dumps. :white_check_mark:
* [Gamecard InitialData](https://switchbrew.org/wiki/XCI#InitialData) area dumps. :white_check_mark:
* [Gamecard CardIdSet](https://switchbrew.org/wiki/Filesystem_services#GameCardIdSet) dumps. :white_check_mark:
* [Gamecard Hash FS partition](https://switchbrew.org/wiki/XCI#PartitionFs) dumps (in both extracted and raw inage forms). :white_check_mark:
* [Lotus ASIC firmware (LAFW) blob](https://switchbrew.org/wiki/Lotus3#User_firmware) dumping from RAM. :white_check_mark:
* Properly detect if an inserted gamecard requires a LAFW update. :white_check_mark:
* Nintendo Submission Package (NSP) dumps for both digital and gamecard-based titles.
* Individual [Ticket](https://switchbrew.org/wiki/Ticket) dumps from digital titles, with support for temporary/volatile tickets with AES-128-CTR crypto data kept in RAM.
* Individual [Nintendo Content Archive (NCA)](https://switchbrew.org/wiki/NCA) dumps from a specific title. :white_check_mark:
* Individual NCA filesystem section ([Partition FS / ExeFS](https://switchbrew.org/wiki/NCA#PFS0), RomFS, Patch RomFS) dumps from a specific NCA belonging to a specific title, in both extracted and raw image forms. :white_check_mark:
* Better support for NCA BucketTree storages (Indirect, AesCtrEx, Sparse, Compressed), as well as better handling of multi-layered reads in combination with game updates. :white_check_mark:
* System title support (e.g. dump qlaunch, shared fonts and much, much more at runtime). :white_check_mark:
* Better memory handling while dealing with compressed NRO binaries. :white_check_mark:
* Event-driven background threads to manage gamecard insertions/ejections, gamecard application metadata parsing (e.g. the game would be properly identified even if its gamecard has never been used on your console), USB ABI sessions and USB Mass Storage devices. :white_check_mark:
* Improved support for multigame gamecards and titles with more than one Program NCA (e.g. SM3DAS). :white_check_mark:
* Control.nacp patching while dumping NSPs (lets you patch screenshot, video, user account and HDCP restrictions). :white_check_mark:
* Partition FS / Hash FS / RomFS browser using custom devoptab wrappers. :white_check_mark:
* Full system update dumps. :x:
* Batch NSP dumps. :x:
* `FsFileSystem` + `FatFs` based eMMC browser using a custom devoptab wrapper (allows copying files protected by the FS sysmodule at runtime). :x:
* New UI using a [customized borealis fork](https://github.com/DarkMatterCore/borealis/tree/nxdumptool-legacy). :warning:
If you like my work and you'd like to support me in any way, it's not necessary, but a donation would be greatly appreciated!
Legend:
[![Donate](https://img.shields.io/static/v1.svg?label=PayPal&message=Donate&color=blue&style=flat&logo=paypal)](https://paypal.me/DarkMatterCore)
Changelog
--------------
**v1.1.8:**
* Added compatibility with latest devkitA64 and libnx releases. Thanks to [HookedBehemoth](https://github.com/HookedBehemoth) for porting the extra IPC calls used by the application to the new IPC system!
* Now using global title contexts instead of global variables for each different title property (ID, version, source storage, etc.). Simplifies metadata retrieval functions.
* Refactored the HFS0/IStorage parsing code, optimizing all gamecard reads performed by the application.
* Increased dump buffer sizes to 4 MiB.
* NCA content size is now calculated and displayed for all titles.
* Content size for updates and DLCs is displayed in the title selector from the NSP menus.
* Additionally, the application now displays the size for each title in the batch dump summary screen. Plus, an *approximate* total dump size is calculated according to the selected titles.
* Please bear in mind that the displayed information does not reflect output NSP dump sizes.
* Changes to the HFS0, ExeFS and RomFS browsers:
* File sizes are now displayed for all file entries.
* Dumping a file/directory won't reset the cursor position anymore.
* Displayed lists are now lexicographically sorted.
* It is now possible to perform CRC32 checksum lookups using the No-Intro database. Big thanks to the folks from No-Intro.org!
* This new method requires a working Internet connection at runtime.
* For XCI dumps, this is merely offered as an alternative to the NSWDB.COM XML database method, without replacing it. A new option has been added to the XCI dump menu, which lets the user select the verification method they wish to use.
* For NSP dumps, on the other hand, this offers a way to actually validate dumps:
* The "CRC32 checksum calculation" feature, which was a bit pointless, has been entirely removed. The new "Verify dump using No-Intro database" option has taken its place.
* NSP dump verification is achieved by just calculating the CRC32 checksum from the output CNMT NCA and performing a lookup using the No-Intro database. This works because of the way CNMT data is handled by the application:
* The SHA-256 checksum for each NCA is always recalculated during the dump process, and the CNMT NCA is always patched afterwards. However, if no NCA modifications are performed, the CNMT NCA ends up being identical to its original counterpart, because the content records won't have changed at all.
* This lets the application verify the NSP dump by performing a CRC32 checksum lookup using the CNMT NCA data, as long as no NCA modifications take place.
* As such, this method only works with SD card / eMMC titles, as long as the "Generate ticket-less dump" option is disabled.
* This option doesn't appear in gamecard-related menus, and it's not compatible with batch dumps.
* By popular demand, an option has been added in XCI, NSP and batch dump menus to change the naming scheme used with output files to the following:
* XCI dumps:
* Single game: `TitleName [TitleID][TitleVersion]`.
* Multigame: `TitleName1 [TitleID1][TitleVersion1] + TitleName2 [TitleID2][TitleVersion2] + ... + TitleNameN [TitleIDN][TitleVersionN]`.
* NSP/Batch dumps: `TitleName [TitleID][TitleVersion][TitleType]`.
* The "Remember dumped titles" feature available in batch mode isn't affected by this new setting - batch overrides will keep using the regular naming scheme.
* Added an option to include delta fragment NCAs in output NSP dumps from installed SD/eMMC updates. It is disabled by default.
* Added a small settings menu to the ExeFS/RomFS sections with the following options:
* `Split files bigger than 4 GiB (FAT32 support)`: unlike previous versions, it is now possible to control if file splitting will take place for ExeFS/RomFS file dumps, instead of always splitting them. If this option is enabled, files bigger than 4 GiB will now be split and stored in a subdirectory with the archive bit set (like NSPs).
* `Save data to CFW directory (LayeredFS)`: enabling this option will save output data to the directory from the CFW you're running, using the LayeredFS layout.
* Added a new option to the batch mode menu to control if the batch dump process should halt on any errors. If disabled, it'll make the batch dump process wait for 5 seconds on any errors, then it will keep going.
* Free SD card space is now always displayed on every UI state. It is also displayed and updated during batch mode operations.
* ExeFS submenu is now available for updates in the orphan content list (Y button menu).
* It is now possible to exit the application from the batch dump summary screen.
* A warning is now displayed in the main menu if the application is launched using applet mode. NSP dumps from base applications and updates can fail if there's not enough heap available to hold the uncompressed `main` NSO while generating the `programinfo.xml`.
* Improved XPath query used when looking for checksum matches in the NSWDB.COM XML database. Fixes CRC32 checksum lookup for multigame cartridges.
* Empty RomFS directories are now properly handled by the RomFS browser.
* Removed a BKTR RomFS section offset check that was causing trouble while trying to perform RomFS-related operations with some updates (e.g. Luigi's Mansion 3).
* Physical IStorage reads are now performed to retrieve NCAs from gamecards, instead of using `ncmContentStorageReadContentIdFile()`. Fixes gamecard NSP/ExeFS/RomFS operations under FW versions < 4.0.0.
* Fixed unaligned IStorage reads in manual file dumps files from HFS0 partitions in gamecards. Unaligned files dumped this way should no longer contain garbage data.
* Fixed a memory leak in the XML database verification code.
* Fixed an indexing bug in the RomFS browser that could potentially cause problems when performing any action from the root directory.
* Fixed gamecard hotswapping in gamecard-related submenus.
* Fixed a free SD card space check in sequential XCI/NSP dump procedures.
* Fixed a bug where the output dump name wouldn't be generated for orphan content when no base applications are installed, preventing the NSP dump procedure from starting. Thanks to [snes878](https://github.com/snes878) for reporting this!
* Fixed a bug that prevented to retrieve the ticket for a bundled-in gamecard update from the Secure HFS0 partition during a NSP dump procedure. Thanks to [snes878](https://github.com/snes878) for reporting this!
* Fixed a bug where a NSP dump process would stop if no personalized ticket certificate is found in the ES system savefile (e.g. when no titles with personalized titlekey crypto have been downloaded from the eShop). Thanks to [satel](https://gbatemp.net/members/satel.27798/) for reporting this!
* Fixed a bug where an empty orphan content list would have been generated if no base applications are installed. Thanks to `Newb_3DS#6287` for reporting this issue!
Thanks to [FennecTECH](https://github.com/fennectech) and MUXI from PSXTools forums for providing with testing!
**v1.1.7:**
* Tickets and RSA certificates are now properly parsed from their respective system savedata files, thanks to the efforts of [shchmue](https://github.com/shchmue)!
* Speeds up ticket / titlekey retrieval for NSP/ExeFS/RomFS operations.
* Removes the need to bundle RSA certificates inside the application. Yay!
* As a bonus, the new `XS00000024` personalized ticket certificate introduced in 9.0.0 is now supported. Thanks to [SimonTime](https://github.com/simontime) for providing insight on this matter!
* Added NSP dump support for pre-installed titles.
* If the selected title uses titlekey crypto and no ticket for it can be found, a prompt will be displayed, asking the user if they want to proceed anyway (even though content decryption won't be possible).
* This prompt will *not* appear in batch dump operations. The dump procedure will always go ahead.
* Sequential NSP dump operations will only display the prompt during their first run.
* Added a new Ticket submenu for SD/eMMC titles. It can be used to only dump the Ticket from a specific base application / update / DLC, without having to dump its entire NSP.
* Dumped tickets are stored in `sdmc:/switch/nxdumptool/Ticket`.
* A configurable option is also available to remove console specific data from dumped tickets.
* The encrypted + decrypted title key is displayed during the dumping process, along with the Rights ID for the title.
* Just so you know, if you want to dump tickets from base application updates bundled in gamecards, use the HFS0 browser.
* Added an option in NSP/batch dump menus to control the replacement of the NPDM RSA key/sig in Program NCAs from base applications and updates:
* Up until now, replacing both the public RSA key in the ACID section from the main.npdm file (ExeFS) and the NPDM header signature (NCA header) has been the default, non-configurable behaviour whenever Program NCA modifications were needed.
* This option is enabled by default - if Program NCA modifications are needed, disabling this option will make the output NSP require ACID patches to function properly under any CFW (but at the same time, it will make the Program NCA verifiable by PC tools).
* The rest of the possible Program NCA modifications (content distribution change and/or Rights ID removal + key area replacement) will be applied when needed, even if this option is disabled.
* Changes related to the orphan content menu (Y button):
* Parent base application name is now retrieved for orphan updates and DLCs whenever possible, and used in menus and output NSP dumps.
* Moved the orphan content hint from the orphan content menu to the SD/eMMC menu.
* Changed application behaviour regarding the Lockpick_RCM keys file existence:
* SD/eMMC menu and NSP/ExeFS/RomFS related operations are now disabled if the keys file at "sdmc:/switch/prod.keys" is not available.
* An error message telling the user to run Lockpick_RCM will be displayed in the main menu if the keys file is not available.
* Additionally, error messages related to data decryption will now also suggest the user to run Lockpick_RCM.
* Changes to the generated update NSPs (thanks to [The-4n](https://github.com/The-4n) and [suchmememanyskill](https://github.com/suchmememanyskill)):
* Delta Fragments are, again, always excluded from output NSP dumps, regardless of their source storage and the selected dump settings.
* Patch Extended Data is no longer wiped from the CNMT NCA in update NSPs - only the content records are replaced accordingly.
* Furthermore, content records from Delta Fragments are preserved as well.
* Fixed CNMT PFS0 block hash calculation when the total PFS0 size exceeds the hash block size from the PFS0 superblock in the NCA header. Removes the `0x236E02` / `2002-4535` error in Goldleaf about an invalid PFS0, triggered by update NSPs with long a CNMT PFS0 section.
* Changes to the generated NSP XMLs:
* `RequiredDownloadSystemVersion` and `IdOffset` elements from the CNMT XML are now properly retrieved from their true locations in the CNMT NCA.
* Added support for the `RuntimeParameterDelivery` NACP field (introduced in HOS 9.X).
* Added support for the `IARCGeneric` value in the `RatingAge` NACP field (introduced in HOS 9.X).
* Fixed handling of `PlayLogQueryableApplicationId` values.
* Big thanks to [0Liam](https://github.com/0Liam) for documenting these changes!
* Changes related to the application update feature:
* Added a forced update prompt if the application is already on the latest version.
* The application update option will now be disabled after a successful update.
* Removed the FS service reinitialize step after closing the application's RomFS at startup. This was done because `romfsExit()` didn't close all open file handles to the NRO when I tested it with libnx v2.2.0 some time ago, thus making the application update fail. Nonetheless, the problem has been fixed.
* Fixed UI flickering when HFS0 partition data can't be retrieved from the gamecard.
* Furthermore, a warning about `nogc` spoofing is now displayed under this particular case.
* Added an extra NSP offset validation step for sequential NSP dumps.
* Minor codestyle fixes.
Big thanks to [FennecTECH](https://github.com/fennectech) and `Hannah (Luna)#8459` for providing with **lots** of testing for this release!
PSA: if you downloaded any new games from the eShop after updating to 9.0.0+ and used a previous release of nxdumptool to dump NSPs **with console specific data**, please redump them - their RSA certificate chain isn't the proper one. Dumps without console specific data (or without a ticket) are not affected by this.
**v1.1.6:**
* Added sequential dump support: it is now possible to start a XCI/NSP dump procedure even if there's not enough space available in the SD card!
* No setting has to be modified in order to enable this feature - the application will automatically ask the user if they want to use this mode if there's not enough space for the full dump.
* At least 1 GiB (2^30 bytes) of free space must be available in order to trigger this feature.
* A file-based checkpoint system is used to keep track of the already dumped parts (à la Hekate).
* The part(s) generated in each run must be transferred to a PC before continuing the process - except for the `.xci.seq`/`.nsp.seq` files used to keep track of the current dump status.
* NSPs generated using this method will also include a `.nsp.hdr` file, which holds the PFS0 header data. The information from this header is filled after writing all the NCAs, thus it is saved as an additional file. This *must* be used as the first file (placed before `.nsp.00`) when merging all the parts into a full NSP.
* The following options are ignored when this feature is triggered:
* `Split output dump (FAT32 support)` (XCI/NSP). File splitting *will* take place, regardless of the filesystem used by the SD card. Additionally, the creation of a directory with the archive bit set isn't performed with NSP dumps.
* `Create directory with archive bit set` (XCI only).
* `CRC32 checksum calculation` (NSP only). CRC32 checksum calculation is still available for XCI dumps.
* This feature is *not* compatible with batch dump operations.
* General changes to batch dump operations:
* Entries from the summary list displayed in the batch dump menu can now be manually excluded from the dump operation before starting it.
* It is possible to disable all entries, enable all entries and/or handpick specific titles from the summary list, thus letting the user further customize the batch dump process.
* A new option has been added to keep track of previous successful dumps created using batch mode: "Remember dumped titles".
* If enabled, a 0-byte file will be created for each successful dump in a separate subdirectory.
* These files act as an override: they will make the application skip the titles they represent in later batch mode operations even if the "Skip already dumped titles" option is disabled.
* This is specially useful if someone wants to skip titles that have already been successfully dumped using batch mode - even more so if their NSPs have already been moved or deleted from the SD card.
* To restore the original behaviour, simply delete the contents from the "BatchOverrides" subdirectory inside "NSP".
* Free storage space is now properly recalculated after each successful dump during a batch mode operation.
* UI code cleanup:
* `uiDrawString()`, `uiGetStrWidth()` and `uiPrintOption()` are now compatible with variable argument lists, removing the need to format a string beforehand and pass its variable to any of those functions.
* Preprocessor definitions are now used to specify RGB colors and for calculating vertical line coordinates, greatly simplifying calls to UI functions.
* Menu code now properly waits for any user input before drawing changes to the screen.
* Other minor coordinate fixes.
* The application is now capable of automatically reading/saving dump settings from/to a configuration file.
* The "Split output dump" option is, once again, enabled by default. FAT32 is the recommended filesystem for Switch SD cards if someone wants to use homebrew applications, so it's only logical to do this.
* Filenames for NACP icons in NSPs now properly reflect the NCA ID from its respective content file if it was modified.
* Fixed a bug that prevented to dump a specific file in the RomFS section from any update.
* Fixed a bug in the RomFS block collision check code that prevented to generate NSP dumps from certain titles with a RomFS section in Control/Manual NCAs that falls under an edge case that wasn't being handled properly. Thanks to [Zet-sensei](https://github.com/Zet-sensei) for reporting this problem!
Thanks to [FennecTECH](https://github.com/fennectech) for providing with testing!
**v1.1.5:**
* Built with latest libnx release, in order to fix HID problems under HOS 9.0.0+.
* Added support for Korean and Chinese character sets.
* Added browsing/dumping support for RomFS sections in Data NCAs from DLCs.
* Compatible with orphan DLCs (Y button) as well.
* Output directories for ExeFS/RomFS operations are now properly tagged as "(BASE)", "(UPD)" or "(DLC)" (RomFS only), depending on the title type being processed.
* Some measures have been taken to help speed up dumping operations:
* CPU boost mode type 1 is now used with `appletSetCpuBoostMode` - only effective under HOS 7.0.0+!
* Removed the need for dynamic memory allocations in NCA AES-CTR block decryption/encryption steps.
* Although these changes get me some extra ~4 MiB/s in most operations, keep in mind this doesn't do much to help with RomFS dumps from titles with lots of (small) file entries. Even so, although the calculated ETA can sometimes be discouraging, the dump most likely *won't* take that much time - just let the process advance until it hits bigger files. Sequential write speeds for the inserted SD card still play a huge role in these cases.
* Moved base output directory from "sdmc:/nxdumptool/" to "sdmc:/switch/nxdumptool/". Both the NSWreleases.xml file and the NRO binary are also expected to be inside this directory.
**v1.1.4:**
* Fixed building with latest libnx release.
* Optimized RomFS recursive file dump function to not rely on code recursion as much as before, avoiding stack memory exhaustion problems. Fixes crashes while dumping RomFS data from games with lots of file entries.
* Fixed a bug that made file splitting not take place while manually dumping a file bigger than 4 GiB from the RomFS section of any title.
* Reduced max part size for split files to `0xFFFF0000` bytes in all operations (except for XCI dumps when the "Create directory with archive bit set" option is disabled). Fixes file access problems if the parts are used inside a directory with the archive bit set.
* Removed the `removeDirectory()` function. `fsdevDeleteDirectoryRecursively()` is now used instead.
* If a HFS0/ExeFS/RomFS data dump operation is cancelled or fails, a message telling the user to wait until the output directory is fully deleted will now be displayed.
* Improved the cancel button detection mechanism. Regardless of the ongoing operation, holding the button for 2 seconds will now consistently cancel it.
* Progress bar movement is now smoother.
**v1.1.3:**
* General changes to the NSP dumping procedure:
* Corrected and updated CNMT XML and NACP XML generation. Thanks to [0Liam](https://github.com/0Liam)!
* Added NACP icon retrieval for each available language.
* Added legalinfo.xml retrieval.
* Added programinfo.xml generation.
* Changed the PFS0 file order to the following:
1. NCA content files.
2. CNMT NCA.
3. CNMT XML.
4. programinfo.xml (if available).
5. NACP icons (if available).
6. NACP XML (if available).
7. legalinfo.xml (if available).
8. Ticket + Certificate chain (if available).
* These changes essentially make the NSP dumps generated by the application comparable to Scene releases that follow the `AuditingTool` format (like those from groups like BigBlueBox or JRP), as long as the "Remove console specific data" option is enabled and the "Generate ticket-less dump" option is disabled. Happy dumping!
* Because of this, dumping update NSPs from gamecards will require the keys file at "sdmc:/switch/prod.keys" from now on (but only if the bundled update uses titlekey crypto). Base applications and DLCs can still be dumped from gamecards without needing a keys file.
* Added ExeFS/RomFS browsing/dumping from game updates.
* Upon entering ExeFS/RomFS menus, it is now possible to select which update is going to be used for ExeFS/RomFS procedures.
* In order to dump ExeFS/RomFS content from a installed update for a gamecard title, its respective gamecard must be inserted in the console.
* Likewise, in order to dump ExeFS/RomFS content from a installed update for a SD/eMMC title, its respective base application must be already installed as well.
* Added NSP batch dump mode. Press X while on the SD/eMMC title list to configure the batch dump options and start the process. Supports skipping already dumped titles, dumping selected title types (base applications, updates, DLCs) and dumping titles from a specific source storage (SD, eMMC).
* Added manual directory dumping feature to the RomFS browser. Just enter the directory to be dumped and then press the Y button.
* Added a forced XCI dump option when either the gamecard base application count or their Title IDs can't be retrieved (useful for rare Kiosk gamecards). Press Y at the error message screen to dump the cartridge image to "gamecard.xci".
* Dumped content information is now displayed in the gamecard menu.
* Additionally, if the XCI has already been dumped, information about it will be displayed as well.
* The displayed information about dumped content is now updated after each new dump procedure in both gamecard and SD/eMMC menus.
* The NPDM ACID patching procedure is now performed with Program NCAs from bundled gamecard updates and SD/eMMC titles if the "Generate ticket-less dump" option is enabled.
* Fixed XCI dumping under SX OS.
* Fixed a bug in the DLC NSP dump submenu that made it impossible to change the DLC to be dumped from the selected base application if more than a single DLC is available for it. Thanks to [ckurtz22](https://github.com/ckurtz22)!
* Fixed a bug that made the application get stuck in an endless loop after selecting the SD/eMMC dump option from the main menu if no SD/eMMC titles are available. Thanks to [ckurtz22](https://github.com/ckurtz22)!
* Fixed a bug that made the application return an empty title list if no SD card is inserted or if it contains a "Nintendo" directory from another console (even if there are installed titles in the eMMC). Thanks to [ckurtz22](https://github.com/ckurtz22)!
**v1.1.2:**
* Delta fragment NCAs are now included in update NSPs dumped from SD/eMMC if the "Generate ticket-less dump" option is disabled.
* It is now possible to generate ticket-less NSP dumps from bundled updates in gamecards. Please bear in mind that this option requires the external "sdmc:/switch/prod.keys" file.
* UI tweaks:
* The application now keeps track of the selected title in SD/eMMC and "orphan" content modes when entering a menu and then going back to the list.
* After selecting a title in the SD/eMMC menu, information about content already dumped related to the selected title will now be displayed (BASE / UPD / DLC).
* Likewise, after selecting a title in the "orphan" title list (Y button), an additional line will now display if the selected title has been dumped or not.
* This also informs the user if the dumps contain console-specific data.
* Three additional entries will now be displayed in the "orphan" title list.
* Upwards and downwards arrows will now be displayed for lists that exceed the max element count.
* Because of this change, max element count for the SD/eMMC title list had to be reduced from 4 to 3.
* Leftwards and rightwards arrowheads are now displayed in menus with options.
* A "hint" message is now displayed in the "orphan" content mode to let the user know they'll be able to find gamecard updates in that section.
* If a file has been already dumped, the application will display a prompt asking the user if they want to proceed anyway or not. This doesn't apply to full HFS0/ExeFS/RomFS data dumps.
* It is now possible to jump from the first list element to the last one and viceversa using the D-Pad Up/Down and Left Stick Up/Down. The Right Stick is still used exclusively for fast scrolling and won't be affected by this change.
* Fixed a bug where NSP/ExeFS/RomFS dumping would fail if the written entry count returned by `ncmContentMetaDatabaseListApplication()` didn't match the total entry count for the selected NSP dump type.
* Fixed a bug where NSP/ExeFS/RomFS dumping would fail if an invalid title index was used with `ncmContentMetaDatabaseGet()`.
Thanks to [Maschell](https://github.com/Maschell), [DuIslingr](https://github.com/DuIslingr) and MUXI from PSXTools forums for reporting these bugs and providing with testing!
**v1.1.1:**
* Project name changed to `nxdumptool`. This is no longer a gamecard-only tool.
* Added ExeFS dumping/browsing support. This feature, along with the already available RomFS options, makes the application an excellent tool for modders!
* Added compatibility with FS process memory layout in the key retrieval procedure while using emuMMC. Thanks to [shchmue](https://github.com/shchmue)!
* Due to public demand, NSP dumping, ExeFS dumping/browsing and RomFS dumping/browsing support has been added for base applications, updates and DLCs available in both SD card and eMMC!
* Now it's possible to select the source storage device (gamecard, SD card / eMMC) for any operation right after launching the application.
* The gamecard submenu works exactly like the main menu has worked up to this point (except for the update options, which are now displayed in the new main menu).
* The SD card / eMMC submenu shows installed base applications along with their icons. Upon selecting a title, a submenu with NSP and RomFS options will show up. It's also possible to dump updates/DLCs for an installed base application this way.
* If there's installed content (updates/DLCs) with missing base application titles, pressing Y on the SD card / eMMC submenu will display this "orphan" content list and let you dump titles from it nonetheless.
* It is possible to generate console-specific NSP dumps, dumps with modified tickets to remove console-specific data, and ticket-less dumps with standard NCA key area crypto.
* Two new options are available in the NSP dump submenus for SD/eMMC titles: "Remove console specific data" and "Generate ticket-less dump". The latter won't appear if the former isn't enabled.
* "Remove console specific data" cleans console specific data fields from a "personalized" ticket and replaces its RSA titlekey block with a 16-byte encrypted titlekey, essentially converting it to a "common" ticket. This option has no effect if the title already uses a "common" ticket.
* "Generate ticket-less dump" goes another step ahead by cleaning up the Rights ID field in every NCA content file that includes it, stores the decrypted titlekey in the NCA key area and then encrypts this area using standard crypto, removing the need for a tik/cert combination.
* Console-specific NSP dumps and dumps with modified tickets include both `tik` and `cert` files.
* All NSP dumps generated from installed SD/eMMC titles include both `.cnmt.xml` and `.nacp.xml` files whenever possible.
* Sadly, due to limitations in the methods currently used to perform key retrieval/derivation at runtime, NSP dumping, ExeFS dumping/browsing and RomFS dumping/browsing for SD/eMMC titles require the "sdmc:/switch/prod.keys" file. Specifically, these are the needed keys:
* `eticket_rsa_kek`.
* `titlekek_##` (varies from `00` to `1F`).
* Additionally, ticket-less NSP dumps for SD/eMMC titles also require the following keys:
* `key_area_key_application_##` (varies from `00` to `1F`).
* `key_area_key_ocean_##` (varies from `00` to `1F`).
* `key_area_key_system_##` (varies from `00` to `1F`).
* All gamecard-related operations can still be performed without the need for a keys file!
* Output data generated by the application will now be saved to its corresponding subdirectory in "sdmc:/nxdumptool/":
* XCI dumps: "sdmc:/nxdumptool/XCI/".
* NSP dumps: "sdmc:/nxdumptool/NSP/".
* HFS0 data: "sdmc:/nxdumptool/HFS0/".
* ExeFS data: "sdmc:/nxdumptool/ExeFS/".
* RomFS data: "sdmc:/nxdumptool/RomFS/".
* Certificate dumps: "sdmc:/nxdumptool/Certificate/".
* The location for the NSWDB.COM XML database has been moved to "sdmc:/nxdumptool/NSWreleases.xml".
* Tickets from updates with titlekey crypto dumped from gamecards are now converted to regular "common" tickets before being written to the output NSP dump.
* The content distribution type for updates dumped from custom XCIs mounted through SX OS is now set to "download".
* Fixed a NCM service handle exhaustion bug if an error ocurred while reading the RomFS section entry from the Program NCA for any base application.
* Changed the application icon yet again. Big thanks to RattletraPM!
* Minor changes and other various general fixes.
Thanks to [simontime](https://github.com/simontime) for helping me out with the RSA certificate chain retrieval process! Also thanks to MUXI from PSXTools forums for providing with testing!
**v1.1.0:**
* Replaced the application icon with a new, stylish one made by RattletraPM. Thanks a lot!
* Gamecard base application icons are now retrieved and displayed in the menu.
* L/ZL/R/ZR buttons can now be used to change the displayed base application info if a multigame cart is inserted, instead of displaying everything right away.
* The Nintendo Extension shared font is now used to display bitmaps representing controller buttons and sticks instead of just using text to reference them.
* Replaced the mbedtls-based AES and SHA-256 implementations with functions from the hardware accelerated cryptography API from libnx.
* Added an option to generate split XCI dumps using a directory with the archive bit set, just like split NSP dumps. It will only appear if "Split output dump" is enabled.
* Fixed ETA calculation.
* Enabled ETA calculation in full HFS0 partition data dumps.
* Fixed CRC32 checksum calculation for gamecard certificate dumps.
* Added Program NCA RomFS section parser:
- Supports filesystem dumping, filesystem browsing, manual file dumping and file splitting. Enjoy datamining your gamecards!
- Compatible with multigame carts. You'll be able to choose which base application RomFS will be dumped/browsed from a submenu.
- Output files will be saved to: "sdmc:/[GameName] v[GameVersion] ([TitleID]) (RomFS)/".
* Added high contrast directory/file icons from GNOME project to file browsing modes (HFS0 / RomFS).
* Fixed the NSP generation code (based on 4NXCI / hacPack):
- Delta Fragment NCAs are now discarded.
- The SHA-256 checksum is recalculated for every NCA content after being modified, resulting in new NCA IDs.
- The ACID public key is replaced in the NPDM section from the Program NCA. All the related NCA/PFS0 Superblock SHA-256 hashes are recalculated.
- The NPDM signature in the Program NCA header is now replaced as well.
- The content records from the Application CNMT are updated with proper SHA-256 hashes and new NCA IDs. All the related NCA/PFS0 Superblock hashes are recalculated.
- NACP XMLs are now generated as well.
- Because of all these changes, the CRC32 checksum can't be calculated until the dump procedure is complete.
- If this option is enabled, the application will take extra time after the NSP dump has been completed to calculate the CRC32 checksum. Nonetheless, you'll be able to cancel this procedure.
- A warning message will appear in the NSP dump menu if CRC32 checksum calculation is enabled to inform the user about this extra step.
- Furthermore, the output CRC32 checksum will be different on each new dump. This is because the NPDM signature in the Program NCA header uses a random seed.
- This effectively makes the generated NSPs only need ES patches to work. ACID patches shouldn't be needed anymore.
* Added NSP dumping support for Patch and AddOnContent title types with gamecards that include bundled Updates/DLCs:
- The information displayed in the main menu now shows how many Updates/DLCs are bundled in the inserted gamecard (per application and in total).
- If a bundled gamecard update features a populated Rights ID bitfield, both its Ticket and Certificate will get added to the output NSP.
- Additionally, the NSP dump menu has been divided in three subcategories: base application, update and DLC.
- Each submenu will only appear if the inserted gamecard holds at least one title belonging to the category it represents.
- If only the base application is included, like most gamecards, choosing the NSP dump option in the main menu will take you right to the base application dump menu.
- Once you enter a submenu, you'll be able to choose exactly which title to dump belonging to that category.
- Output update NSPs will not be modified in any way. Thus, unlike NSPs from base applications and DLCs, their CRC32 checksums will always be the same.
* Fixed the minimum system version field size in the extended CNMT header struct. Thanks to [0Liam](https://github.com/0Liam)!
* Changed the naming convention for output NSP dumps:
- Base application: "sdmc:/[GameName] v[GameVersion] ([TitleID]) (BASE).nsp".
- Update: "sdmc:/[GameName] v[UpdateVersion] ([UpdateTitleID]) (UPD).nsp".
- If a matching base application isn't found: "sdmc:/[UpdateTitleID] v[UpdateVersion] (UPD).nsp".
- DLC: "sdmc:/[GameName] v[DLCVersion] ([DLCTitleID]) (DLC).nsp".
- If a matching base application isn't found: "sdmc:/[DLCTitleID] v[DLCVersion] (DLC).nsp".
* The application is now able to retrieve the NCA header key and perform NCA key area decryption at runtime, using the SPL services. Thus, is isn't needed to run Lockpick beforehand anymore to dump NSPs (nor to dump/browse RomFS data).
* If the inserted gamecard includes a bundled update, its version number will now be used in the output filename for XCI, HFS0 and gamecard certificate dumps.
* Minor improvements to the file splitting code.
- Additionally, the filename for the current part will now be displayed and updated for all operations if file splitting is enabled.
* The application update feature will now use the launch path from argv if it's available. Otherwise, it defaults to "sdmc:/switch/gcdumptool.nro".
* Cosmetic fixes to the UI layout.
* NCM service resources are now properly closed.
* Removed unnecessary service (de)initializations.
Big thanks to PatrickD85, unvaluablespace, wartutor and Slim45 for testing these changes!
**v1.0.8:**
* Added proper metadata reading from multigame carts.
* Added gamecard -> NSP dump option:
- Compatible with file splitting (for FAT32 support). The same layout from splitNSP.py is used: a directory with numbered part files (00, 01, etc.). The archive bit is enabled right away in this directory to allow HOS to treat it as if it were a whole file. This way, it can be used with any application with NSP-handling capabilities.
- Compatible with CRC32 checksum calculation. Disclaimer: NSP dumps can't be verified against the XML database.
- Output NSPs contain a metadata XML file based on the information from the CNMT NCA for the application, which is decrypted using code from hactool. The necessary keyset is loaded from "sdmc:/switch/prod.keys", which can be generated using Lockpick.
- If a multigame cart is used, you'll be able to choose which application to dump from the menu.
* Dump verification process tweaked for multigame carts: it'll now look for a possible checksum match using the Title IDs from all bundled applications.
* Improved error reporting in dumper.c when a write operation fails. Furthermore, if a write error is produced when trying to write data to an offset past the FAT32 file size limit (0xFFFFFFFF bytes), the application will suggest the user to enable the file splitting option.
* Tweaked part sizes for splitted dumps: XCI/raw partition/manual file dump part size now matches the one used by XCI-Cutter, while the NSP part size matches the one used by splitNSP.py.
* Minor fixes to the UI code.
**v1.0.7:**
* Fixed a segmentation fault when trying to free an invalid XML node data pointer when a Scene release from NSWReleases.xml with a matching Title ID misses data related to that node.
* Added a message suggesting the user to restart the application after a successful update.
**v1.0.6:**
* Updated application codebase in order to make it compatible with the latest devkitA64 and libnx releases.
* Removed some fs-srv service functions from fsext.c/h that have been included in libnx (and fixed the ones that haven't).
* Revamped the GFX code to replace the 8x8 ASCII font with the shared system font, using the pl service and FreeType.
* Enabled (and fixed) the in-app update option. HTTPS compatibility is achieved through the mbedtls portlib.
* Disabled screen dimming and auto sleep.
* Added file counter to partition browser.
* Changed the naming convention for split gamecard dumps to *.xc[part number], in order to make them compatible with SX OS and other tools right away.
* Increased the delay after inserting a new gamecard by 1 second.
* Added a gamecard detection thread to monitor gamecard state changes in a better way. This thread is hooked to a gamecard detection kernel handle retrieved through an IEventNotifier object.
* Replaced partition filesystem mounting through fs-srv service calls with manual HFS0 partition header parsing. This should fix issues when browsing the Logo partition from type 0x02 gamecards.
* Blocked HOME button presses when running as a regular/system application instead of an applet. A warning message will be displayed whenever any operation is started if the application is running as an applet.
* Added detection for bundled FW versions 6.0.0 - 8.0.0.
**v1.0.5:**
* Fixed gamecard version reading (now using the ncm service instead of retrieving it from the cached Control.nacp).
* Added ability to read and identify FW update versions bundled with gamecards.
* In case an error occurs while reading the gamecard Title ID, the application will also display the FW version update bundled with it along with an explanation.
* Removed output XCI dump renaming based on the XML database from nswdb.com.
* Output naming scheme changed. Characters out of the ASCII range are replaced with underscores:
- XCI dump: "sdmc:/[GameName] v[GameVersion] ([TitleID]).xci".
- Raw partition dump: "sdmc:/[GameName] v[GameVersion] ([TitleID]) - Partition [PartitionIndex] ([PartitionName]).hfs0".
- Partition data dump (directory): "sdmc:/[GameName] v[GameVersion] ([TitleID]) - Partition [PartitionIndex] ([PartitionName])/".
- Certificate dump: "sdmc:/[GameName] v[GameVersion] ([TitleID]) - Certificate ([CRC32]).bin".
* Manual file dumps will now be saved to their corresponding directory instead of the SD card root.
* Added a XML database update option in the main menu.
* Added an update application option in the main menu. It isn't working at this moment because libcurl has problems dealing with secure connections, and as such the option has been disabled (pressing A on it does nothing). Nonetheless, the code to parse JSON responses from the GitHub API is pretty much ready, so it's just a matter of time.
**v1.0.4:**
* exFAT mode turned on by default.
* Replaced padding option with a trim output dump option (same as XCI-Cutter).
* Added dump speed and ETA calculation.
* Added XCI dump verification using XML database from nswdb.com (NSWreleases.xml). The file must be saved to the SD card root directory. Also, keep in mind that dump verification is only performed if you choose to create a full dump (with or without cert), not a trimmed one.
* Made CRC32 checksum calculation + XCI dump verification a configurable option.
* Output XCI dumps will get renamed to their corresponding Scene release if a match is found using the XML database from nswdb.com (e.g. "sdmc:/0100000000010000_20180625-234930.xci" -> "sdmc:/Super.Mario.Odyssey.NSW-BigBlueBox.xci").
**v1.0.3:**
* Made the 0xFF padding feature a configurable option.
* Added CRC32 checksum calculation for XCI dumps.
**v1.0.2:**
* Fixed a silly bug in the file splitting code.
**v1.0.1:**
* Minor UI fixes and tweaks.
* Added some missing Title ID checks in uiLoop().
* All calls to uiStatusMsg() are now properly identified.
* Increased wait time to 2 seconds when a new gamecard is detected.
**v1.0.0:**
Initial release.
* :white_check_mark: - Implemented
* :warning: - Partially implemented
* :x: - Not implemented

View File

@ -1,172 +0,0 @@
# gcdumptool
닌텐도 스위치 게임 카드 덤프 도구
주요 기능
--------------
* 선택적 인증서 제거 및 선택적인 트리밍을 통해 전체 카트리지 이미지 덤프 (XCI)를 생성.
* 카트리지 응용 프로그램에서 설치 가능 패키지 (NSP)를 생성.
- Lockpick을 사용하여 미리 전체 NCA 키 집합을 검색해야합니다. "sdmc:/switch/prod.keys"에 저장해야합니다.
* 멀티 게임 카트 지원.
* XCI/NSP 덤프에 대한 CRC32 체크섬 계산.
* NSWDB.COM (NSWreleases.xml)의 XML 데이터베이스를 사용하여 전체 XCI 덤프 확인.
* libcurl을 통한 XML 데이터베이스, 인앱 업데이트.
* 게임 카드에서 루트 HFS0 헤더를 사용하여 정확한 HFS0 원시 파티션 덤프.
* HFS0 파티션 파일 데이터 덤프.
* 수동 파일 덤프 지원하는 HFS0 파티션 파일 브라우저.
* RomFS 섹션 파일 데이터 덤핑.
* 수동 파일 덤프 지원하는 RomFS 섹션 파일 브라우저.
* 수동 게임 카드 인증서 덤프.
* 잔여 SD 카드 공간 확인.
* 모든 작업에 대한 파일 분할 지원.
* NCM 및 NS 서비스를 사용한 게임 카드 메타 데이터 검색.
* 덤프 속도, ETA 계산, 진행률 표시 줄.
감사
--------------
* MCMrARM, 오리지널 응용 프로그램 제작.
* RSDuck, vba-next-switch 포트 용. UI 메뉴 코드는이 응용 프로그램의 기초로 사용.
* Foen, NCM 서비스를 사용하는 방법에 대한 좋은 힌트 제공.
* Yellows8, 일부 NCM 서비스 IPC 호출을 구현할 때 버그를 수정하도록 도움.
* SciresM, hactool. AES 암호 처리 및 외부 키 파일 구문 분석 코드는 NSP 덤프 프로세스 중에 사용.
* The-4n, 4NXCI 및 hacPack 용. 4NXCI에서 사용된 NCA 내용 패치 절차는 hacPack의 NACP XML 생성과 응용 프로그램 복제
* shchmue, Lockpick 용. NSP 덤프 및 RomFS 덤프/탐색 절차에 필요한 키 수집 알고리즘에 대한 참조로 사용
* Björn Samuelsson, C에 대한 공개 도메인 CRC32 체크섬 계산 코드 (crc32_fast.c).
* AnalogMan, 지속적인 지지와 아이디어.
* RattletraPM, 응용 프로그램에 사용되는 멋진 아이콘.
* GNOME 프로젝트, 파일 브라우저 모드에 대한 고대비 디렉토리 / 파일 아이이 검색.
* ReSwitched의 사람들은 좋은 홈브류 에코 시스템을 창조하기 위해 노력.
변경이력
--------------
**v1.1.0:**
* RattletraPM이 만든 새롭고 세련된 응용 프로그램 아이콘으로 대체. 고맙습니다!
* 게임 카드 기본 응용 프로그램 아이콘이 검색되어 메뉴에 표시.
* 모든 항목을 즉시 표시하는 대신 멀티 게임 카트가 삽입되어 있으면 L/ZL/R/ZR 버튼을 사용하여 표시된 기본 응용 프로그램 정보를 변경할 수 있음.
* 닌텐도 확장 공유 글꼴은 이제 텍스트를 사용하는 대신 컨트롤러 버튼과 스틱을 나타내는 비트 맵을 표시하는데 사용.
* mbedtls 기반 AES 및 SHA-256 구현을 libnx의 하드웨어 가속 암호화 API의 기능으로 대체.
* 분할 NSP 덤프처럼 보관 비트가 설정된 디렉토리를 사용하여 분할 XCI 덤프를 생성하는 옵션 추가. "분할 출력 덤프"가 활성화 된 경우에만 나타남.
* ETA 계산 수정.
* 전체 HFS0 파티션 데이터 덤프에서 ETA 계산 활성화.
* 게임카드 인증서 덤프에 대한 CRC32 체크섬 계산 수정.
* NCA RomFS 섹션 파서 추가 된 프로그램 :
- 파일 시스템 덤프, 파일 시스템 브라우징, 수동 파일 덤핑, 파일 분할 지원. 당신의 게임카드 데이터 수집을 즐기십시오!
- 멀티 게임 카트와 호환. 하위 메뉴에서 덤프/브라우즈 할 기본 응용 프로그램을 선택할 수 있음.
- 출력 파일은 다음과 같이 저장: "sdmc:/[GameName] v[GameVersion] ([TitleID]) (RomFS)/".
* 그놈 프로젝트에서 파일 찾아보기 모드 (HFS0/RomFS)에 고대비 디렉토리/파일 아이콘 추가.
* NSP 생성 코드 (4NXCI/hacPack 기반) 수정.
- 델타 조각은 이제 삭제.
    - SHA-256 체크섬은 수정 된 후 모든 NCA 콘텐츠에 대해 다시 계산되어 새로운 NCA ID가 생성.
    - ACID 공개 키는 프로그램 NCA의 NPDM 섹션에서 대체. 관련된 모든 NCA/PFS0 수퍼 블록 SHA-256 해시가 다시 계산.
- 프로그램 NCA 헤더의 NPDM 서명이 이제 대체
- 응용 프로그램 CNMT의 콘텐츠 레코드는 적절한 SHA-256 해시 및 새 NCA ID로 업데이트. 관련된 모든 NCA/PFS0 수퍼 블록 해시가 다시 계산.
    - 이제 NACP XML도 생성.
- 이러한 모든 변경 때문에 CRC32 체크섬은 덤프 프로시저가 완료될 때까지 계산할 수 없음.
- 이 옵션을 사용하면 응용 프로그램이 CRC32 체크섬을 계산하기 위해 NSP 덤프가 완료된 후 추가 시간이 소요. 그럼에도 불구하고 이 절차를 취소 할 수 있음.
- CRC32 체크섬 계산이 활성화되어 NSP 덤프 메뉴에 경고 메시지가 표시되면 사용자에게 이 추가 단계를 알림.
- 또한 출력 CRC32 체크섬은 새로운 덤프마다 다를 수 있음. 프로그램 NCA 헤더의 NPDM 서명이 임의 시드를 사용하기 때문.
- 이로 인해 효과적으로 생성된 NSP는 ES 패치만 작동하면 됨. ACID 패치는 더 이상 필요하지 않음.
* 번들로 제공되는 업데이트/DLC를 포함하는 게임카드로 패치 및 AddOnContent 타이틀 유형에 대한 NSP 덤핑 지원 추가:
    - 주 메뉴에 표시된 정보에는 삽입 된 게임 카드에 번들로 제공되는 업데이트 / DLC의 수 (응용 프로그램 및 총)가 표시.
- 번들로 제공되는 게임카드 업데이트에 권한이 부여된 비트맵이 있는 경우 해당 티켓과 인증서가 모두 출력 NSP에 추가.
- 또한 NSP 덤프 메뉴는 기본 응용 프로그램, 업데이트, DLC의 세 가지 하위 범주로 나뉨.
- 각 하위 메뉴는 삽입된 게임카드가 해당 범주에 속한 타이틀을 하나 이상 보유한 경우에만 나타남.
- 대부분의 게임카드처럼 기본 응용 프로그램만 포함되어 있으면 메뉴에서 NSP 덤프 옵션을 선택하면 기본 응용 프로그램 덤프 메뉴로 바로 이동할 수 있음.
- 하위 메뉴에 들어가면 해당 카테고리에 속하는 덤프할 타이틀을 정확하게 선택할 수 있습니다.
- 출력 업데이트 NSP는 어떤 방식으로도 수정되지 않기에 기본 응용 프로그램 및 DLC의 NSP와 달리 CRC32 체크섬은 항상 동일.
* 확장된 CNMT 헤더 구조체의 최소 시스템 버전 필드 크기 수정. @0Liam 감사합니다!
* 출력 NSP 덤프의 명명 규칙 변경:
- 기본 응용 프로그램: "sdmc:/[GameName] v[GameVersion] ([TitleID]) (BASE).nsp".
- 엡데이트: "sdmc:/[GameName] v[UpdateVersion] ([UpdateTitleID]) (UPD).nsp".
- 일치하는 기본 응용 프로그램을 찾을 수 없는 경우: "sdmc:/[UpdateTitleID] v[UpdateVersion] (UPD).nsp".
- DLC: "sdmc:/[GameName] v[DLCVersion] ([DLCTitleID]) (DLC).nsp".
- 일치하는 기본 응용 프로그램을 찾을 수 없는 경우: "sdmc:/[DLCTitleID] v[DLCVersion] (DLC).nsp".
* 이제 응용 프로그램은 SPL 서비스를 사용하여 NCA 헤더 키를 검색하고 런타임에 NCA 키 영역 암호 해독을 수행 할 수 있습니다. 따라서 NSP를 덤프하거나 (RomFS 데이터를 덤프 / 브라우즈하지 않기 위해) 미리 Lockpick을 실행하지 않아도됩니다.
* 삽입된 게임카드에 번들로 제공되는 업데이트가 포함되어있는 경우 해당 버전 번호가 이제 XCI, HFS0, 게임카드 인증서 덤프의 출력 파일 이름에 사용.
* 파일 분할 코드의 사소한 개선.
    - 파일 분할이 활성화 된 경우 현재 작업의 파일 이름이 표시되고 모든 작업에 대해 업데이트.
* 응용 프로그램 업데이트 기능은 argv에서 실행 경로를 사용할 수 있는 경우 이를 사용합니다. 그렇지 않은 경우 기본값은 "sdmc:/switch/gcdumptool.nro" 입니다.
* UI 레이아웃 수정.
* NCM 서비스 자원이 이제 제대로 닫힘.
* 불필요한 서비스 (탈)초기화가 없어짐.
이러한 변화를 테스트 한 PatrickD85, unvaluablespace, wartutor, Slim45에게 큰 감사를 드립니다!
**v1.0.8:**
* 멀티 게임 카트에서 적절한 메타 데이터 읽기 추가.
* gamecard -> NSP 덤프 옵션 추가:
- 파일 분할과 호환 (FAT32 지원). splitNSP.py와 동일한 레이아웃이 사용: 번호가 매겨진 파트 파일 (00, 01 등)이 있는 디렉토리. 아카이브 비트는 이 디렉토리에서 즉시 활성화되어 HOS가 전체 파일인 것처럼 처리할 수 있음. 이 방법은 NSP 처리 기능이 있는 모든 응용 프로그램에서 사용할 수 있음.
- CRC32 체크섬 계산과 호환. 면책 조항: NSP 덤프는 XML 데이터베이스에 대해 확인할 수 없음.
- 출력 NSP에는 응용 프로그램에 대한 CNMT NCA의 정보를 기반으로 메타 데이터 XML 파일이 들어 있으며 hactool의 코드를 사용하여 암호 해독. 필요한 키셋은 "sdmc:/switch/prod.keys"에서 로드되며 Lockpick을 사용하여 생성할 수 있음.
- 멀티 게임 카트를 사용하면 메뉴에서 덤프할 응용 프로그램을 선택할 수 있음.
* 덤프 확인 프로세스가 멀티 게임 카트에 맞게 조정. 이제 번들로 제공되는 모든 응용 프로그램의 타이틀 ID를 사용하여 가능한 체크섬 일치를 찾음.
* 쓰기 조작이 실패할 때 dumper.c의 향상된 오류보고. 또한 FAT32 파일 크기 제한 (0xFFFFFFFF 바이트)을 넘은 오프셋에 데이터를 쓰려고 할 때 쓰기 오류가 발생하면 응용 프로그램에서 사용자에게 파일 분할 옵션을 사용하도록 제안.
* 분할된 덤프의 부품 크기 조정: XCI/raw partition/수동 파일 덤프 파트 크기가 이제 XCI-Cutter에서 사용되는 파트 크기와 일치하지만 NSP 파트 크기는 splitNSP.py에서 사용되는 부품 크기와 일치
* UI 코드에 대한 사소한 수정.
**v1.0.7:**
* 타이틀 ID가 일치하는 NSWReleases.xml의 Scene 릴리스가 해당 노드와 관련된 데이터를 누락한 경우 유효하지 않은 XML 노드 데이터 포인터를 해제하려고 시도할 때 세그먼트화 오류 수정
* 업데이트 성공 후 사용자에게 응용 프로그램을 다시 시작하라는 메시지를 추가.
**v1.0.6:**
* 최신 devkitA64 및 libnx 릴리즈와 호환되도록 응용 프로그램 코드베이스 업데이트.
* libnx에 포함되어있는 fsext.c/h에서 일부 fs-srv 서비스 기능 제거 (아직 수정하지 않은 기능을 수정).
* GFX 코드를 개조하여 pl 서비스와 FreeType을 사용하여 8x8 ASCII 글꼴을 공유 시스템 글꼴 대체.
* 인앱 업데이트 옵션을 활성화 (및 수정). HTTPS 호환성은 mbedtls portlib를 통해 이루어짐.
* 비활성화된 화면 디밍 및 자동 절전.
* 파티션 브라우저에 파일 카운터 추가.
* 분할된 게임 카드 덤프의 이름 지정 규칙을 SX OS 및 기타 도구와 즉시 호환되도록 *.xc[부품 번호]로 변경.
* 새로운 게임카드를 1 초 삽입한 후 지연 시간이 증가.
* 게임카드 상태 변경 사항을 보다 나은 방법으로 모니터링 할 수 있는 게임카드 탐지 스레드 추가. 이 스레드는 IEventNotifier 개체를 통해 검색된 게임카드 검색 커널 핸들에 연결.
* 수동 HFS0 파티션 헤더 구문 분석으로 fs-srv 서비스 호출을 통해 파티션 된 파일 시스템 마운트 대체. 이 경우 0x02 유형의 게임카드에서 로고 파티션을 탐색할 때 문제 수정.
* 애플릿 대신 일반/시스템 응용 프로그램으로 실행 중일 때 홈 버튼이 눌려 있습니다. 응용 프로그램이 애플릿으로 실행중인 경우 작업이 시작될 때마다 경고 메시지가 표시.
* 번들로 제공되는 FW 버전 6.0.0 - 8.0.0에 대한 감지 추가.
**v1.0.5:**
* 캐시된 Control.nacp에서 ncm 서비스를 검색하는 대신 ncm 서비스를 사용하는 게임 카드 버전 읽기 수정.
* 게임 카드와 함께 제공되는 FW 업데이트 버전을 읽고 식별하는 기능 추가.
* 게임 카드 타이틀 ID를 읽는 중 오류가 발생하는 경우 응용 프로그램은 설명과 함께 번들로 제공되는 FW 버전 업데이트도 표시
* nswdb.com의 XML 데이터베이스를 기반으로 출력 XCI 덤프 이름 바꾸기가 제거.
* 출력 명명 체계가 변경. ASCII 범위를 벗어나는 문자는 밑줄로 변경:
- XCI 덤프: "sdmc:/[GameName] v[GameVersion] ([TitleID]).xci".
- 원시 파티션 덤프: "sdmc:/[GameName] v[GameVersion] ([TitleID]) - 파티션 [PartitionIndex] ([PartitionName]).hfs0".
- 파티션 데이터 덤프 (디렉토리): "sdmc:/[GameName] v[GameVersion] ([TitleID]) - 파티션 [PartitionIndex] ([PartitionName])/".
- 인증서 덤프: "sdmc:/[GameName] v[GameVersion] ([TitleID]) - Certificate ([CRC32]).bin".
* 수동 파일 덤프는 이제 SD 카드 루트가 아닌 해당 디렉토리에 저장.
* 주 메뉴에 XML 데이터베이스 업데이트 옵션 추가
* 주 메뉴에 업데이트 응용 프로그램 옵션 추가. libcurl은 보안 연결을 다루는데 문제가 있기 때문에 현재로서는 작동하지 않음. 따라서 옵션이 비활성화되었습니다 (A를 누르면 아무 것도하지 않습니다). 그럼에도 불구하고 GitHub API에서 JSON 응답을 구문 분석하는 코드는 거의 준비가되어 있으므로 시간 문제 일뿐.
**v1.0.4:**
* exFAT 모드는 기본적으로 켜져 있음.
* 패딩 옵션을 트림 출력 덤프 옵션으로 변경 (XCI-Cutter와 동일).
* 덤프 속도 및 ETA 계산 추가..
* nswdb.com (NSWreleases.xml)의 XML 데이터베이스를 사용하여 XCI 덤프 확인 추가. 파일은 SD 카드 루트 디렉토리에 저장함. 또한 덤프 검증은 전체 덤프 (인증서 포함 또는 제외)를 작성하기로 한 경우에만 수행되며 절단되지 않은 경우에는 수행되지 않음.
* CRC32 체크섬 계산 + XCI 덤프 확인을 구성 가능한 옵션 제작.
* nswdb.com의 XML 데이터베이스를 사용하여 일치하는 항목이 발견되면 출력 XCI 덤프의 이름이 해당 Scene 릴리스로 변경 (예: "sdmc:/0100000000010000_20180625-234930.xci"-> "sdmc:/Super.Mario.Odyssey.NSW- BigBlueBox.xci").
**v1.0.3:**
* 0xFF 패딩 기능을 구성 가능한 옵션 제작.
* XCI 덤프에 대한 CRC32 체크섬 계산 추가.
**v1.0.2:**
* 파일 분할 코드에서 바보같은 버그 수정.
**v1.0.1:**
* 사소한 UI 수정 및 조정.
* uiLoop()에서 누락 된 타이틀 ID 확인을 일부 추가.
* uiStatusMsg()에 대한 모든 호출이 이제 올바르게 식별.
* 새로운 gamecard가 감지되면 대기 시간이 2 초로 증가.
**v1.0.0:**
첫 릴리즈.

47
build.sh Normal file
View File

@ -0,0 +1,47 @@
#!/bin/bash
ARG=${1:-'--confirm'}
cd "$(dirname "${BASH_SOURCE[0]}")"
# Clean-up from last build
rm -rf ./code_templates/tmp
mkdir ./code_templates/tmp
mv ./source/main.cpp ./main.cpp
make clean_all
# Build PoC
poc_name="nxdt_rw_poc"
poc_path="./code_templates/$poc_name.c"
rm -f ./source/main.c
cp $poc_path ./source/main.c
cp ./romfs/icon/nxdumptool.jpg ./romfs/icon/$poc_name.jpg
set -e
if [ ${ARG,,} != "--noconfirm" ]; then
make BUILD_TYPE="$poc_name" -j$(nproc)
else
make BUILD_TYPE="$poc_name" -j8
fi
set +e
rm -f ./romfs/icon/$poc_name.jpg
mv -f ./$poc_name.nro ./code_templates/tmp/$poc_name.nro
mv -f ./$poc_name.elf ./code_templates/tmp/$poc_name.elf
# Post build clean-up
make BUILD_TYPE="$poc_name" clean
make clean_all
rm -f ./source/main.c
mv -f ./main.cpp ./source/main.cpp
if [ ${ARG,,} != "--noconfirm" ]; then
read -rsp $'Press any key to continue...\n' -n 1 key
fi

View File

@ -0,0 +1,80 @@
if (g_titleInfo && g_titleInfoCount)
{
mkdir("sdmc:/records", 0777);
FILE *title_infos_txt = NULL, *icon_jpg = NULL;
char icon_path[FS_MAX_PATH] = {0};
title_infos_txt = fopen("sdmc:/records/title_infos.txt", "wb");
if (title_infos_txt)
{
for(u32 i = 0; i < g_titleInfoCount; i++)
{
fprintf(title_infos_txt, "Storage ID: 0x%02X\r\n", g_titleInfo[i].storage_id);
fprintf(title_infos_txt, "Title ID: %016lX\r\n", g_titleInfo[i].meta_key.id);
fprintf(title_infos_txt, "Version: %u (%u.%u.%u-%u.%u)\r\n", g_titleInfo[i].meta_key.version, g_titleInfo[i].version.major, \
g_titleInfo[i].version.minor, g_titleInfo[i].version.micro, g_titleInfo[i].version.major_relstep, \
g_titleInfo[i].version.minor_relstep);
fprintf(title_infos_txt, "Type: 0x%02X\r\n", g_titleInfo[i].meta_key.type);
fprintf(title_infos_txt, "Install Type: 0x%02X\r\n", g_titleInfo[i].meta_key.install_type);
fprintf(title_infos_txt, "Title Size: %s (0x%lX)\r\n", g_titleInfo[i].size_str, g_titleInfo[i].size);
fprintf(title_infos_txt, "Content Count: %u\r\n", g_titleInfo[i].content_count);
for(u32 j = 0; j < g_titleInfo[i].content_count; j++)
{
char content_id_str[SHA256_HASH_SIZE + 1] = {0};
utilsGenerateHexStringFromData(content_id_str, sizeof(content_id_str), g_titleInfo[i].content_infos[j].content_id.c, sizeof(g_titleInfo[i].content_infos[j].content_id.c), false);
u64 content_size = 0;
ncmContentInfoSizeToU64(&(g_titleInfo[i].content_infos[j]), &content_size);
char content_size_str[32] = {0};
utilsGenerateFormattedSizeString(content_size, content_size_str, sizeof(content_size_str));
fprintf(title_infos_txt, " Content #%u:\r\n", j + 1);
fprintf(title_infos_txt, " Content ID: %s\r\n", content_id_str);
fprintf(title_infos_txt, " Content Size: %s (0x%lX)\r\n", content_size_str, content_size);
fprintf(title_infos_txt, " Content Type: 0x%02X\r\n", g_titleInfo[i].content_infos[j].content_type);
fprintf(title_infos_txt, " ID Offset: 0x%02X\r\n", g_titleInfo[i].content_infos[j].id_offset);
}
if (g_titleInfo[i].app_metadata)
{
TitleApplicationMetadata *app_metadata = g_titleInfo[i].app_metadata;
if (strlen(app_metadata->lang_entry.name)) fprintf(title_infos_txt, "Name: %s\r\n", app_metadata->lang_entry.name);
if (strlen(app_metadata->lang_entry.author)) fprintf(title_infos_txt, "Author: %s\r\n", app_metadata->lang_entry.author);
if (g_titleInfo[i].meta_key.type == NcmContentMetaType_Application && app_metadata->icon_size && app_metadata->icon)
{
fprintf(title_infos_txt, "JPEG Icon Size: 0x%X\r\n", app_metadata->icon_size);
sprintf(icon_path, "sdmc:/records/%016lX.jpg", app_metadata->title_id);
icon_jpg = fopen(icon_path, "wb");
if (icon_jpg)
{
fwrite(app_metadata->icon, 1, app_metadata->icon_size, icon_jpg);
fclose(icon_jpg);
icon_jpg = NULL;
utilsCommitSdCardFileSystemChanges();
}
}
}
if (g_titleInfo[i].meta_key.type == NcmContentMetaType_Patch || g_titleInfo[i].meta_key.type == NcmContentMetaType_AddOnContent)
{
if (g_titleInfo[i].previous) fprintf(title_infos_txt, "Previous %s ID: %016lX\r\n", g_titleInfo[i].meta_key.type == NcmContentMetaType_Patch ? "Patch" : "AOC", g_titleInfo[i].previous->meta_key.id);
if (g_titleInfo[i].next) fprintf(title_infos_txt, "Next %s ID: %016lX\r\n", g_titleInfo[i].meta_key.type == NcmContentMetaType_Patch ? "Patch" : "AOC", g_titleInfo[i].next->meta_key.id);
}
fprintf(title_infos_txt, "\r\n");
fflush(title_infos_txt);
}
fclose(title_infos_txt);
title_infos_txt = NULL;
utilsCommitSdCardFileSystemChanges();
}
}

7108
code_templates/nxdt_rw_poc.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,471 @@
/*
* main.c
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "nxdt_utils.h"
#include "gamecard.h"
#include "title.h"
#include "cnmt.h"
#include "program_info.h"
#include "nacp.h"
#include "legal_info.h"
bool g_borealisInitialized = false;
static PadState g_padState = {0};
static void utilsScanPads(void)
{
padUpdate(&g_padState);
}
static u64 utilsGetButtonsDown(void)
{
return padGetButtonsDown(&g_padState);
}
static u64 utilsGetButtonsHeld(void)
{
return padGetButtons(&g_padState);
}
static void utilsWaitForButtonPress(u64 flag)
{
/* Don't consider stick movement as button inputs. */
if (!flag) flag = ~(HidNpadButton_StickLLeft | HidNpadButton_StickLRight | HidNpadButton_StickLUp | HidNpadButton_StickLDown | HidNpadButton_StickRLeft | HidNpadButton_StickRRight | \
HidNpadButton_StickRUp | HidNpadButton_StickRDown);
while(appletMainLoop())
{
utilsScanPads();
if (utilsGetButtonsDown() & flag) break;
}
}
static void consolePrint(const char *text, ...)
{
va_list v;
va_start(v, text);
vfprintf(stdout, text, v);
va_end(v);
consoleUpdate(NULL);
}
static void writeFile(void *buf, size_t buf_size, const char *path)
{
FILE *fd = fopen(path, "wb");
if (fd)
{
fwrite(buf, 1, buf_size, fd);
fclose(fd);
utilsCommitSdCardFileSystemChanges();
}
}
int main(int argc, char *argv[])
{
NX_IGNORE_ARG(argc);
NX_IGNORE_ARG(argv);
int ret = EXIT_SUCCESS;
if (!utilsInitializeResources())
{
ret = EXIT_FAILURE;
goto out;
}
/* Configure input. */
/* Up to 8 different, full controller inputs. */
/* Individual Joy-Cons not supported. */
padConfigureInput(8, HidNpadStyleSet_NpadFullCtrl);
padInitializeWithMask(&g_padState, 0x1000000FFUL);
consoleInit(NULL);
u32 app_count = 0;
TitleApplicationMetadata **app_metadata = NULL;
TitleUserApplicationData user_app_data = {0};
u32 selected_idx = 0, page_size = 30, scroll = 0;
bool applet_status = true, exit_prompt = true;
NcaContext *nca_ctx = NULL;
Ticket tik = {0};
u32 meta_idx = 0;
ContentMetaContext cnmt_ctx = {0};
u32 program_count = 0, program_idx = 0;
ProgramInfoContext *program_info_ctx = NULL;
u32 control_count = 0, control_idx = 0;
NacpContext *nacp_ctx = NULL;
u32 legal_info_count = 0, legal_info_idx = 0;
LegalInfoContext *legal_info_ctx = NULL;
char path[FS_MAX_PATH] = {0};
app_metadata = titleGetApplicationMetadataEntries(false, &app_count);
if (!app_metadata || !app_count)
{
consolePrint("app metadata failed\n");
goto out2;
}
consolePrint("app metadata succeeded\n");
utilsSleep(1);
while((applet_status = appletMainLoop()))
{
consoleClear();
printf("select a user application to generate xmls for.\npress b to exit.\n\n");
printf("title: %u / %u\n", selected_idx + 1, app_count);
printf("selected title: %016lX - %s\n\n", app_metadata[selected_idx]->title_id, app_metadata[selected_idx]->lang_entry.name);
for(u32 i = scroll; i < app_count; i++)
{
if (i >= (scroll + page_size)) break;
printf("%s%016lX - %s\n", i == selected_idx ? " -> " : " ", app_metadata[i]->title_id, app_metadata[i]->lang_entry.name);
}
printf("\n");
consoleUpdate(NULL);
u64 btn_down = 0, btn_held = 0;
while((applet_status = appletMainLoop()))
{
utilsScanPads();
btn_down = utilsGetButtonsDown();
btn_held = utilsGetButtonsHeld();
if (btn_down || btn_held) break;
if (titleIsGameCardInfoUpdated())
{
free(app_metadata);
app_metadata = titleGetApplicationMetadataEntries(false, &app_count);
if (!app_metadata)
{
consolePrint("\napp metadata failed\n");
goto out2;
}
selected_idx = scroll = 0;
break;
}
}
if (!applet_status) break;
if (btn_down & HidNpadButton_A)
{
if (!titleGetUserApplicationData(app_metadata[selected_idx]->title_id, &user_app_data) || !user_app_data.app_info)
{
consolePrint("\nthe selected title doesn't have available base content.\n");
utilsSleep(3);
titleFreeUserApplicationData(&user_app_data);
continue;
}
break;
} else
if ((btn_down & HidNpadButton_Down) || (btn_held & (HidNpadButton_StickLDown | HidNpadButton_StickRDown)))
{
selected_idx++;
if (selected_idx >= app_count)
{
if (btn_down & HidNpadButton_Down)
{
selected_idx = scroll = 0;
} else {
selected_idx = (app_count - 1);
}
} else
if (selected_idx >= (scroll + (page_size / 2)) && app_count > (scroll + page_size))
{
scroll++;
}
} else
if ((btn_down & HidNpadButton_Up) || (btn_held & (HidNpadButton_StickLUp | HidNpadButton_StickRUp)))
{
selected_idx--;
if (selected_idx == UINT32_MAX)
{
if (btn_down & HidNpadButton_Up)
{
selected_idx = (app_count - 1);
scroll = (app_count >= page_size ? (app_count - page_size) : 0);
} else {
selected_idx = 0;
}
} else
if (selected_idx < (scroll + (page_size / 2)) && scroll > 0)
{
scroll--;
}
} else
if (btn_down & HidNpadButton_B)
{
exit_prompt = false;
goto out2;
}
if (btn_held & (HidNpadButton_StickLDown | HidNpadButton_StickRDown | HidNpadButton_StickLUp | HidNpadButton_StickRUp)) svcSleepThread(50000000); // 50 ms
}
if (!applet_status)
{
exit_prompt = false;
goto out2;
}
consoleClear();
consolePrint("selected title:\n%s (%016lX)\n\n", app_metadata[selected_idx]->lang_entry.name, app_metadata[selected_idx]->title_id);
nca_ctx = calloc(user_app_data.app_info->content_count, sizeof(NcaContext));
if (!nca_ctx)
{
consolePrint("nca ctx calloc failed\n");
goto out2;
}
consolePrint("nca ctx calloc succeeded\n");
meta_idx = (user_app_data.app_info->content_count - 1);
program_count = titleGetContentCountByType(user_app_data.app_info, NcmContentType_Program);
if (program_count && !(program_info_ctx = calloc(program_count, sizeof(ProgramInfoContext))))
{
consolePrint("program info ctx calloc failed\n");
goto out2;
}
control_count = titleGetContentCountByType(user_app_data.app_info, NcmContentType_Control);
if (control_count && !(nacp_ctx = calloc(control_count, sizeof(NacpContext))))
{
consolePrint("nacp ctx calloc failed\n");
goto out2;
}
legal_info_count = titleGetContentCountByType(user_app_data.app_info, NcmContentType_LegalInformation);
if (legal_info_count && !(legal_info_ctx = calloc(legal_info_count, sizeof(LegalInfoContext))))
{
consolePrint("legal info ctx calloc failed\n");
goto out2;
}
for(u32 i = 0, j = 0; i < user_app_data.app_info->content_count; i++)
{
// set meta nca as the last nca
NcmContentInfo *content_info = &(user_app_data.app_info->content_infos[i]);
if (content_info->content_type == NcmContentType_Meta) continue;
if (!ncaInitializeContext(&(nca_ctx[j]), user_app_data.app_info->storage_id, (user_app_data.app_info->storage_id == NcmStorageId_GameCard ? HashFileSystemPartitionType_Secure : 0), \
&(user_app_data.app_info->meta_key), content_info, &tik))
{
consolePrint("%s #%u initialize nca ctx failed\n", titleGetNcmContentTypeName(content_info->content_type), content_info->id_offset);
goto out2;
}
consolePrint("%s #%u initialize nca ctx succeeded\n", titleGetNcmContentTypeName(content_info->content_type), content_info->id_offset);
if (nca_ctx[j].fs_ctx[0].has_sparse_layer) continue;
switch(content_info->content_type)
{
case NcmContentType_Program:
if (!programInfoInitializeContext(&(program_info_ctx[program_idx]), &(nca_ctx[j])))
{
consolePrint("initialize program info ctx failed (%s)\n", nca_ctx[j].content_id_str);
goto out2;
}
nca_ctx[j].content_type_ctx = &(program_info_ctx[program_idx++]);
break;
case NcmContentType_Control:
if (!nacpInitializeContext(&(nacp_ctx[control_idx]), &(nca_ctx[j])))
{
consolePrint("initialize nacp ctx failed (%s)\n", nca_ctx[j].content_id_str);
goto out2;
}
nca_ctx[j].content_type_ctx = &(nacp_ctx[control_idx++]);
break;
case NcmContentType_LegalInformation:
if (!legalInfoInitializeContext(&(legal_info_ctx[legal_info_idx]), &(nca_ctx[j])))
{
consolePrint("initialize legal info ctx failed (%s)\n", nca_ctx[j].content_id_str);
goto out2;
}
nca_ctx[j].content_type_ctx = &(legal_info_ctx[legal_info_idx++]);
break;
default:
break;
}
j++;
}
if (!ncaInitializeContext(&(nca_ctx[meta_idx]), user_app_data.app_info->storage_id, (user_app_data.app_info->storage_id == NcmStorageId_GameCard ? HashFileSystemPartitionType_Secure : 0), \
&(user_app_data.app_info->meta_key), titleGetContentInfoByTypeAndIdOffset(user_app_data.app_info, NcmContentType_Meta, 0), &tik))
{
consolePrint("meta nca initialize ctx failed\n");
goto out2;
}
consolePrint("meta nca initialize ctx succeeded\n");
if (!cnmtInitializeContext(&cnmt_ctx, &(nca_ctx[meta_idx])))
{
consolePrint("cnmt initialize ctx failed\n");
goto out2;
}
consolePrint("cnmt initialize ctx succeeded\n");
sprintf(path, "sdmc:/at_xml/%016lX", app_metadata[selected_idx]->title_id);
utilsCreateDirectoryTree(path, true);
if (cnmtGenerateAuthoringToolXml(&cnmt_ctx, nca_ctx, user_app_data.app_info->content_count))
{
consolePrint("cnmt xml succeeded\n");
sprintf(path, "sdmc:/at_xml/%016lX/%s.cnmt.xml", app_metadata[selected_idx]->title_id, cnmt_ctx.nca_ctx->content_id_str);
writeFile(cnmt_ctx.authoring_tool_xml, cnmt_ctx.authoring_tool_xml_size, path);
} else {
consolePrint("cnmt xml failed\n");
}
for(u32 i = 0; i < user_app_data.app_info->content_count; i++)
{
NcaContext *cur_nca_ctx = &(nca_ctx[i]);
if (!cur_nca_ctx->content_type_ctx || cur_nca_ctx->content_type == NcmContentType_Meta) continue;
switch(cur_nca_ctx->content_type)
{
case NcmContentType_Program:
{
ProgramInfoContext *cur_program_info_ctx = (ProgramInfoContext*)cur_nca_ctx->content_type_ctx;
if (!programInfoGenerateAuthoringToolXml(cur_program_info_ctx))
{
consolePrint("program info xml failed (%s | id offset #%u)\n", cur_nca_ctx->content_id_str, cur_nca_ctx->id_offset);
goto out2;
}
consolePrint("program info xml succeeded (%s | id offset #%u)\n", cur_nca_ctx->content_id_str, cur_nca_ctx->id_offset);
sprintf(path, "sdmc:/at_xml/%016lX/%s.programinfo.xml", app_metadata[selected_idx]->title_id, cur_nca_ctx->content_id_str);
writeFile(cur_program_info_ctx->authoring_tool_xml, cur_program_info_ctx->authoring_tool_xml_size, path);
break;
}
case NcmContentType_Control:
{
NacpContext *cur_nacp_ctx = (NacpContext*)cur_nca_ctx->content_type_ctx;
if (!nacpGenerateAuthoringToolXml(cur_nacp_ctx, user_app_data.app_info->version.value, cnmtGetRequiredTitleVersion(&cnmt_ctx)))
{
consolePrint("nacp xml failed (%s | id offset #%u)\n", cur_nca_ctx->content_id_str, cur_nca_ctx->id_offset);
goto out2;
}
consolePrint("nacp xml succeeded (%s | id offset #%u)\n", cur_nca_ctx->content_id_str, cur_nca_ctx->id_offset);
//sprintf(path, "sdmc:/at_xml/%016lX/%s.nacp", app_metadata[selected_idx]->title_id, cur_nca_ctx->content_id_str);
//writeFile(cur_nacp_ctx->data, sizeof(_NacpStruct), path);
sprintf(path, "sdmc:/at_xml/%016lX/%s.nacp.xml", app_metadata[selected_idx]->title_id, cur_nca_ctx->content_id_str);
writeFile(cur_nacp_ctx->authoring_tool_xml, cur_nacp_ctx->authoring_tool_xml_size, path);
for(u8 j = 0; j < cur_nacp_ctx->icon_count; j++)
{
NacpIconContext *icon_ctx = &(cur_nacp_ctx->icon_ctx[j]);
sprintf(path, "sdmc:/at_xml/%016lX/%s.nx.%s.jpg", app_metadata[selected_idx]->title_id, cur_nca_ctx->content_id_str, nacpGetLanguageString(icon_ctx->language));
writeFile(icon_ctx->icon_data, icon_ctx->icon_size, path);
}
break;
}
case NcmContentType_LegalInformation:
{
LegalInfoContext *cur_legal_info_ctx = (LegalInfoContext*)cur_nca_ctx->content_type_ctx;
sprintf(path, "sdmc:/at_xml/%016lX/%s.legalinfo.xml", app_metadata[selected_idx]->title_id, cur_nca_ctx->content_id_str);
writeFile(cur_legal_info_ctx->authoring_tool_xml, cur_legal_info_ctx->authoring_tool_xml_size, path);
consolePrint("legal info xml succeeded (%s | id offset #%u)\n", cur_nca_ctx->content_id_str, cur_nca_ctx->id_offset);
break;
}
default:
break;
}
}
out2:
if (exit_prompt)
{
consolePrint("press any button to exit\n");
utilsWaitForButtonPress(0);
}
if (legal_info_ctx)
{
for(u32 i = 0; i < legal_info_count; i++) legalInfoFreeContext(&(legal_info_ctx[i]));
free(legal_info_ctx);
}
if (nacp_ctx)
{
for(u32 i = 0; i < control_count; i++) nacpFreeContext(&(nacp_ctx[i]));
free(nacp_ctx);
}
if (program_info_ctx)
{
for(u32 i = 0; i < program_count; i++) programInfoFreeContext(&(program_info_ctx[i]));
free(program_info_ctx);
}
cnmtFreeContext(&cnmt_ctx);
if (nca_ctx) free(nca_ctx);
titleFreeUserApplicationData(&user_app_data);
if (app_metadata) free(app_metadata);
out:
utilsCloseResources();
consoleExit(NULL);
return ret;
}

279
host/README.md Normal file
View File

@ -0,0 +1,279 @@
# nxdumptool USB Application Binary Interface (ABI) Technical Specification
This Markdown document aims to explain the technical details behind the ABI used by nxdumptool to communicate with a USB host device connected to the console. As of this writing (November 11th, 2023), the current ABI version is `1.2`.
In order to avoid unnecessary clutter, this document assumes the reader is already familiar with homebrew launching on the Nintendo Switch, as well as USB concepts such as device/configuration/interface/endpoint descriptors and bulk mode transfers. Shall this not be the case, a small list of helpful resources is available at the end of this document.
Unless stated otherwise, the reader must assume all integer fields in the documented structs follow a little-endian (LE) order.
## Table of contents
* [USB device interface details](#usb-device-interface-details).
* [USB driver](#usb-driver).
* [Unix-like operating systems](#unix-like-operating-systems).
* [Windows](#windows).
* [USB communication details](#usb-communication-details).
* [Command header](#command-header).
* [Command IDs](#command-ids).
* [Command blocks](#command-blocks).
* [StartSession](#startsession).
* [SendFileProperties](#sendfileproperties).
* [CancelFileTransfer](#cancelfiletransfer).
* [SendNspHeader](#sendnspheader).
* [EndSession](#endsession).
* [StartExtractedFsDump](#startextractedfsdump).
* [EndExtractedFsDump](#endextractedfsdump).
* [Status response](#status-response).
* [Status codes](#status-codes).
* [NSP transfer mode](#nsp-transfer-mode).
* [Why is there such thing as a 'NSP transfer mode'?](#why-is-there-such-thing-as-a-nsp-transfer-mode)
* [Zero Length Termination (ZLT)](#zero-length-termination-zlt).
* [Additional resources](#additional-resources).
## USB device interface details
Right after launching nxdumptool on the target Nintendo Switch, the application configures the console's USB interface using the following information:
* Device descriptor:
* Class / Subclass / Protocol: all set to `0x00` (defined at the interface level).
* Vendor ID: `0x057E`.
* Product ID: `0x3000`.
* BCD release number: `0x0100`.
* Product string: `nxdumptool`.
* Manufacturer string: `DarkMatterCore`.
* Multiple device descriptors are used to support USB 1.1, 2.0 and 3.0 speeds, each one with slightly different properties. The underlying USB stack from a USB host device usually takes care of automatically choosing one of these, depending on the capabilities of the USB host.
* USB 3.0 support depends on the `usb30_force_enabled` setting from Horizon OS to be manually enabled before launching a custom firmware (CFW) on the target console. Otherwise, only 1.1 and 2.0 speeds will be made available to the USB host device.
* Configuration descriptor:
* A single configuration descriptor is provided, regardless of the USB speed selected by the USB host.
* Interface descriptor:
* A single interface descriptor with no alternate setting is provided as part of the configuration descriptor.
* Class / Subclass / Protocol: all set to `0xFF` (vendor-specific).
* Endpoint descriptors:
* Only two bulk endpoint descriptors are provided as part of the interface descriptor.
* The max packet size varies depending on the USB speed selected by the USB host:
* USB 1.1: 64 bytes.
* USB 2.0: 512 bytes.
* USB 3.0: 1024 bytes.
* SuperSpeed endpoint companion descriptors are also provided if USB 3.0 is used.
* Binary Object Store (BOS) descriptor:
* Holds a USB 2.0 extension descriptor for Link Power Management (LPM) support, as well as a SuperSpeed device capability descriptor to indicate the supported speeds.
Communication is performed through the bulk input and output endpoints using 10-second timeouts.
Verifying the product string is not required at this moment -- this is because PoC builds of the rewrite branch use a different `APP_TITLE` string.
## USB driver
A USB driver is needed to actually communicate to the target console running nxdumptool.
### Unix-like operating systems
A package manager can be used to install [libusb](https://libusb.info), which in turn can be used by programs to enumerate and interact with the target console. Under some operating systems, this step may not even be needed.
### Windows
A tool such as [Zadig](https://zadig.akeo.ie) must be used to manually install a USB driver for the target console.
Even though it's possible to use the `WinUSB` driver, we suggest to use `libusbK` instead -- the provided Python script in this directory depends on [PyUSB](https://github.com/pyusb/pyusb), which only provides a backend for `libusb` devices. If you intend to write your own `WinUSB`-based ABI host implementation for Windows based on this document, you may be able to use that driver.
Furthermore, even though it's possible for USB devices to work right out of the box using [Microsoft OS descriptors](https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors), the `usb:ds` API available to homebrew applications on the Nintendo Switch doesn't provide any way to set them. Thus, it's not possible to interact with the target console without installing a USB driver first.
## USB communication details
The USB host device essentially acts as a storage server for nxdumptool. This means all commands are initially issued by the target console, leading to data transfer stages for which status responses are expected to be sent by the USB host device.
This intends to minimize the overhead on the USB host device by letting nxdumptool take care of the full dump process -- the host only needs to take care of storing the received data. This also heavily simplifies the work required to write ABI host implementations from scratch, regardless of the programming language being used.
Command handling can be broken down in three different transfer stages: command header (from nxdumptool), command block (from nxdumptool) and status response (from USB host). Certain commands may lead to an additional data transfer stage after the status response is received from the USB host device.
### Command header
Size: 0x10 bytes.
| Offset | Size | Type | Description |
|--------|------|--------------|------------------------------------------------|
| 0x00 | 0x04 | `uint32_t` | Magic word (`NXDT`) (`0x5444584E`). |
| 0x04 | 0x04 | `uint32_t` | [Command ID](#command-ids). |
| 0x08 | 0x04 | `uint32_t` | Command block size. |
| 0x0C | 0x04 | `uint8_t[4]` | Reserved. |
While handling ABI commands, nxdumptool first issues the command header -- this way, the USB host device knows both the command ID and the command block size before attempting to receive the command block.
Certain commands yield no command block at all, leading to a command block size of zero -- this is considered defined behaviour. Nonetheless, a status response is still expected to be sent by the USB host.
#### Command IDs
| Value | Name | Description |
|-------|-------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------|
| 0 | [`StartSession`](#startsession) | Starts a USB session between the target console and the USB host device. |
| 1 | [`SendFileProperties`](#sendfileproperties) | Sends file metadata and starts a data transfer process. |
| 2 | [`CancelFileTransfer`](#cancelfiletransfer) | Cancels an ongoing data transfer process started by a previously issued [`SendFileProperties`](#sendfileproperties) command. |
| 3 | [`SendNspHeader`](#sendnspheader) | Sends the `PFS0` header from a Nintendo Submission Package (NSP). Only issued under [NSP transfer mode](#nsp-transfer-mode). |
| 4 | [`EndSession`](#endsession) | Ends a previously stablished USB session between the target console and the USB host device. |
| 5 | [`StartExtractedFsDump`](#startextractedfsdump) | Informs the host device that an extracted filesystem dump (e.g. HFS, PFS, RomFS) is about to begin. |
| 6 | [`EndExtractedFsDump`](#endextractedfsdump) | Informs the host device that a previously started filesystem dump (via [`StartExtractedFsDump`](#startextractedfsdump)) has finished. |
### Command blocks
All commands, with the exception of `CancelFileTransfer` and `EndSession`, yield a command block. Each command block follows its own distinctive structure.
#### StartSession
Size: 0x10 bytes.
| Offset | Size | Type | Description |
|--------|------|--------------|---------------------------------------------------------------------|
| 0x00 | 0x01 | `uint8_t` | nxdumptool version (major). |
| 0x01 | 0x01 | `uint8_t` | nxdumptool version (minor). |
| 0x02 | 0x01 | `uint8_t` | nxdumptool version (micro). |
| 0x03 | 0x01 | `uint8_t` | nxdumptool USB ABI version (high nibble: major, low nibble: minor). |
| 0x04 | 0x08 | `char[8]` | Git commit hash (NULL-terminated string). |
| 0x0C | 0x04 | `uint8_t[4]` | Reserved. |
This is the first USB command issued by nxdumptool upon connection to a USB host device. If it succeeds, further USB commands may be sent.
#### SendFileProperties
Size: 0x320 bytes.
| Offset | Size | Type | Description |
|--------|-------|---------------|----------------------------------------------|
| 0x000 | 0x008 | `uint64_t` | File size. |
| 0x008 | 0x004 | `uint32_t` | Path length. |
| 0x00C | 0x004 | `uint32_t` | [NSP header size](#nsp-transfer-mode). |
| 0x010 | 0x301 | `char[769]` | UTF-8 encoded path (NULL-terminated string). |
| 0x311 | 0x00F | `uint8_t[15]` | Reserved. |
Sent right before starting a file transfer. If it succeeds, a data transfer stage will take place using 8 MiB (0x800000) chunks. If needed, the last chunk will be truncated.
A status response is expected from the USB host right after receiving this command block, which is also right before starting the file data transfer stage. Furthermore, an additional status response is expected right after the last file data chunk has been sent.
The `path` field uses forward slashes (`/`) as separators, and it will always begin with one. Its contents represent a relative path (e.g. `/NSP/Doki Doki Literature Club Plus 1.0.3 [010086901543E800][v196608][UPD].nsp`) generated by nxdumptool for any of its output storage devices, which is usually appended to an actual output directory path (e.g. `sdmc:/switch/nxdumptool`).
Illegal Windows filesystem characters (`\`, `/`, `:`, `*`, `?`, `"`, `<`, `>`, `|`) are replaced by underscores (`_`) in each path element by nxdumptool itself before sending the command block.
Furthermore, the USB host is free to decide how to handle the relative path (e.g. create full directory tree in a user-defined output directory, entirely disregard the path and only keep the filename, etc.).
If the last chunk size from the data transfer stage is aligned to the endpoint max packet size, the USB host should expect a [ZLT packet](#zero-length-termination-zlt).
Finally, it should be noted that it's possible for the `filesize` field to be zero, in which case the host device shall only create the file and send a single status response right away.
#### CancelFileTransfer
Yields no command block. Expects a status response, just like the rest of the commands.
This command can only be issued under two different scenarios:
* During the file data transfer stage from a [SendFileProperties](#sendfileproperties) command.
* In-between two different [SendFileProperties](#sendfileproperties) commands while under [NSP transfer mode](#nsp-transfer-mode).
It is used to gracefully cancel an ongoing file transfer while also keeping the USB session alive. It's up to the USB host to decide what to do with the incomplete data.
The easiest way to detect this command during a file transfer is by checking the length of the last received block and then parse it to see if it matches a `CancelFileTransfer` command header.
#### SendNspHeader
Variable length. The command block size from the command header represents the NSP header size, while the command block data represents the `PFS0` header from a NSP.
If the NSP header size is aligned to the endpoint max packet size, the USB host should expect a [ZLT packet](#zero-length-termination-zlt).
For more information, read the [NSP transfer mode](#nsp-transfer-mode) section of this document.
#### EndSession
Yields no command block. Expects a status response, just like the rest of the commands.
This command is only issued while exiting nxdumptool, as long as the target console is connected to a host device and a USB session has been successfully established.
#### StartExtractedFsDump
Size: 0x310 bytes.
| Offset | Size | Type | Description |
|--------|-------|---------------|----------------------------------------------------------------|
| 0x000 | 0x008 | `uint64_t` | Extracted FS dump size. |
| 0x008 | 0x301 | `char[769]` | UTF-8 encoded extracted FS root path (NULL-terminated string). |
| 0x309 | 0x006 | `uint8_t[6]` | Reserved. |
Sent right before dumping a Switch FS in extracted form (e.g. HFS, PFS, RomFS) using multiple [SendFileProperties](#sendfileproperties) commands in succession.
The extracted FS dump size field can be used by the host device to calculate an ETA for the overall FS dump.
The extracted FS root path follows the same conventions as the `path` field from a [`SendFileProperties`](#sendfileproperties) command, which means it also represents a relative path to an actual output directory used by nxdumptool, but with the difference that it actually points to the directory where all the extracted FS entries will be stored. In other words, all file paths from the extracted FS dump will begin with this string.
A new `StartExtractedFsDump` command will never be issued unless an ongoing extracted FS dump is either cancelled (via [`CancelFileTransfer`](#cancelfiletransfer)) or finished (via [`EndExtractedFsDump`](#endextractedfsdump)).
This command is mutually exclusive with the [NSP transfer mode](#nsp-transfer-mode) -- it'll never be issued if this mode is active.
#### EndExtractedFsDump
Yields no command block. Expects a status response, just like the rest of the commands.
This command is only issued after all file entries from an extracted FS dump (started via [`StartExtractedFsDump`](#startextractedfsdump)) have been successfully transferred to the host device.
If a [`CancelFileTransfer`](#cancelfiletransfer) command is issued before finishing an extracted FS dump, this command shall not be expected.
This command is mutually exclusive with the [NSP transfer mode](#nsp-transfer-mode) -- it'll never be issued if this mode is active.
### Status response
Size: 0x10 bytes.
| Offset | Size | Type | Description |
|--------|------|--------------|-------------------------------------|
| 0x00 | 0x04 | `uint32_t` | Magic word (`NXDT`) (`0x5444584E`). |
| 0x04 | 0x04 | `uint32_t` | [Status code](#status-codes). |
| 0x08 | 0x02 | `uint16_t` | Endpoint max packet size. |
| 0x0A | 0x06 | `uint8_t[6]` | Reserved. |
Status responses are expected by nxdumptool at certain points throughout the command handling steps:
* Right after receiving a command header and/or command block (depending on the command ID).
* Right after receiving the last file data chunk from a [SendFileProperties](#sendfileproperties) command.
The endpoint max packet size must be sent back to the target console using status responses because `usb:ds` API's `GetUsbDeviceSpeed` cmd is only available under Horizon OS 8.0.0+. We want to provide USB communication support under lower versions, even if it means we have to resort to measures like this one.
#### Status codes
| Value | Description |
|-------|------------------------------------------------------------------|
| 0 | Success. |
| 1 | Invalid command size. Reserved for internal nxdumptool usage. |
| 2 | Failed to write command. Reserved for internal nxdumptool usage. |
| 3 | Failed to read status. Reserved for internal nxdumptool usage. |
| 4 | Invalid magic word. |
| 5 | Unsupported command. |
| 6 | Unsupported USB ABI version. |
| 7 | Malformed command. |
| 8 | USB host I/O error (write error, insufficient space, etc.). |
### NSP transfer mode
If the NSP header size field from a [SendFileProperties](#sendfileproperties) command block is greater than zero, the USB host should enter NSP transfer mode. The file size field from this block represents, then, the full NSP size (including the NSP header).
In this mode, the USB host should immediately create the output file, write `NSP header size` bytes of padding to it, reply with a status response as usual and expect further [SendFileProperties](#sendfileproperties) commands. No file data is transferred for this very first [SendFileProperties](#sendfileproperties) command block.
Each further [SendFileProperties](#sendfileproperties) command block will hold the filename and size for a specific NSP file entry, and the NSP header size field will always be set to zero. The file data received for each one of these file entries must be written to the output file created during the first [SendFileProperties](#sendfileproperties) command. The sum of all file entry sizes should be equal to the full NSP size minus the NSP header size received during the first [SendFileProperties](#sendfileproperties) command.
Finally, the USB host will receive a [SendNspHeader](#sendnspheader) command with the NSP header data, which should be written at the start of the output file. The command block size in the command header should match the NSP header size received in the first [SendFileProperties](#sendfileproperties) command.
#### Why is there such thing as a 'NSP transfer mode'?
This is because the `PFS0` header from NSPs holds the filenames for all file entries written into the package, which are mostly [Nintendo Content Archives (NCA)](https://switchbrew.org/wiki/NCA).
NCA filenames represent the first half of the NCA SHA-256 checksum, in lowercase. This fact alone makes it impossible to send a NSP header right from the beginning -- SHA-256 checksums are calculated by nxdumptool while dumping each NCA.
#### Zero Length Termination (ZLT)
As per USB bulk transfer specification, when a USB host/device receives a data packet smaller than the endpoint max packet size, it shall consider the transfer is complete and no more data packets are left. This is called a transaction completion mechanism.
However, if the last data chunk is aligned to the endpoint max packet size, an alternate completion mechanism is needed -- this is where Zero Length Termination (ZLT) packets come into play. If this condition is met, the USB host device should expect a single ZLT packet from nxdumptool right after the last data chunk has been transferred.
If no ZLT packet were issued, the USB stack from the host device wouldn't be capable of knowing the ongoing transfer has been completed, making it expect further data to be sent by the target console -- which in turn leads to a timeout error on the USB host side. Furthermore, if the ZLT packet is left unhandled by the USB host device, a timeout error will be raised on the target console's side.
Most USB backend implementations require the host application to provide a bigger read size (+1 byte at least) if a ZLT packet is to be expected from the connected device. This should be more than enough.
## Additional resources
* [USB in a NutShell](https://www.beyondlogic.org/usbnutshell/usb1.shtml).
* [USB Made Simple](https://www.usbmadesimple.co.uk).

17
host/install_deps.py Normal file
View File

@ -0,0 +1,17 @@
# Copyright (c) 2019-2021 Ian Burgwin
# This is meant to be double-clicked from File Explorer.
# This doesn't import pip as a module in case the way it's executed changes, which it has in the past.
# Instead we call it like we would in the command line.
from subprocess import run
from os.path import dirname, join
from sys import executable
from platform import system
root_dir = dirname(__file__)
requirements_file = ('requirements-win32.txt' if system() == 'Windows' else 'requirements.txt')
run([executable, '-m', 'pip', 'install', '-r', join(root_dir, requirements_file)])
input('Press enter to close')

BIN
host/nxdt.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

1440
host/nxdt_host.py Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,2 @@
-r requirements.txt
comtypes>=1.1.11

2
host/requirements.txt Normal file
View File

@ -0,0 +1,2 @@
tqdm>=4.59.0
pyusb>=1.1.1

View File

@ -0,0 +1,26 @@
@echo off
set scriptdir=%~dp0
set scriptdir=%scriptdir:~0,-1%
set venvname=standalone
set venvscripts=%scriptdir%\%venvname%\Scripts
set venvpython=%venvscripts%\python.exe
cd /D "%scriptdir%"
python -m venv "%venvname%"
"%venvpython%" -m pip install --upgrade nuitka -r requirements-win32.txt
"%venvpython%" -m nuitka --standalone --deployment --disable-console --windows-icon-from-ico=nxdt.ico --enable-plugin=tk-inter nxdt_host.py
del /F /Q nxdt_host.7z
7z a nxdt_host.7z .\nxdt_host.dist\*
rmdir /s /q nxdt_host.build
rmdir /s /q nxdt_host.dist
rmdir /s /q standalone
pause

97
include/core/aes.h Normal file
View File

@ -0,0 +1,97 @@
/*
* aes.h
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __AES_H__
#define __AES_H__
#ifdef __cplusplus
extern "C" {
#endif
/// One-shot function to perform AES-128-ECB crypto.
/// 'dst', 'src' and 'key' must all have a size of at least AES_BLOCK_SIZE bytes.
/// 'dst' and 'src' can both point to the same address.
void aes128EcbCrypt(void *dst, const void *src, const void *key, bool encrypt);
/// Performs an AES-128-XTS crypto operation using the non-standard Nintendo XTS tweak.
/// The Aes128XtsContext element should have been previously initialized with aes128XtsContextCreate(). 'encrypt' should match the value of 'is_encryptor' used with that call.
/// 'dst' and 'src' can both point to the same address.
size_t aes128XtsNintendoCrypt(Aes128XtsContext *ctx, void *dst, const void *src, size_t size, u64 sector, size_t sector_size, bool encrypt);
/// Initializes an output AES partial counter using an initial CTR value and an offset.
/// The sizes for 'out' and 'ctr' should be at least AES_BLOCK_SIZE and 8 bytes, respectively.
NX_INLINE void aes128CtrInitializePartialCtr(u8 *out, const u8 *ctr, u64 offset)
{
if (!out || !ctr) return;
offset >>= 4;
for(u8 i = 0; i < 8; i++)
{
out[i] = ctr[0x8 - i - 1];
out[0x10 - i - 1] = (u8)(offset & 0xFF);
offset >>= 8;
}
}
/// Updates the provided AES partial counter using an offset.
/// Size for 'out' should be at least AES_BLOCK_SIZE.
NX_INLINE void aes128CtrUpdatePartialCtr(u8 *ctr, u64 offset)
{
if (!ctr) return;
offset >>= 4;
for(u8 i = 0; i < 8; i++)
{
ctr[0x10 - i - 1] = (u8)(offset & 0xFF);
offset >>= 8;
}
}
/// Updates the provided AES partial counter using an offset and a 32-bit CTR value.
/// Size for 'out' should be at least AES_BLOCK_SIZE.
NX_INLINE void aes128CtrUpdatePartialCtrEx(u8 *ctr, u32 ctr_val, u64 offset)
{
if (!ctr) return;
offset >>= 4;
for(u8 i = 0; i < 8; i++)
{
ctr[0x10 - i - 1] = (u8)(offset & 0xFF);
offset >>= 8;
}
for(u8 i = 0; i < 4; i++)
{
ctr[0x8 - i - 1] = (u8)(ctr_val & 0xFF);
ctr_val >>= 8;
}
}
#ifdef __cplusplus
}
#endif
#endif /* __AES_H__ */

63
include/core/bfttf.h Normal file
View File

@ -0,0 +1,63 @@
/*
* bftff.h
*
* Copyright (c) 2018, simontime.
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __BFTTF_H__
#define __BFTTF_H__
#ifdef __cplusplus
extern "C" {
#endif
/// Loosely based on PlSharedFontType.
typedef enum {
BfttfFontType_Standard = 0, ///< Japan, US and Europe.
BfttfFontType_NintendoExt = 1, ///< Extended Nintendo. This font contains special, Nintendo-specific characters, which aren't available in the other fonts.
BfttfFontType_Korean = 2, ///< Korean (Hangul).
BfttfFontType_ChineseSimplified = 3, ///< Simplified Chinese.
BfttfFontType_ExtChineseSimplified = 4, ///< Extended Simplified Chinese.
BfttfFontType_ChineseTraditional = 5, ///< Traditional Chinese.
BfttfFontType_Count = 6 ///< Total fonts supported by this enum.
} BfttfFontType;
/// Loosely based on PlFontData.
typedef struct {
u8 type; ///< BfttfFontType.
u32 size; ///< Decoded BFTFF font size.
void *address; ///< Font data address.
} BfttfFontData;
/// Initializes the BFTTF interface.
bool bfttfInitialize(void);
/// Closes the BFTTF interface.
void bfttfExit(void);
/// Returns a specific BFTTF font using the provided BfttfFontType.
bool bfttfGetFontByType(BfttfFontData *font, u8 font_type);
#ifdef __cplusplus
}
#endif
#endif /* __BFTTF_H__ */

249
include/core/bktr.h Normal file
View File

@ -0,0 +1,249 @@
/*
* bktr.h
*
* Copyright (c) 2018-2020, SciresM.
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __BKTR_H__
#define __BKTR_H__
#include "nca.h"
#ifdef __cplusplus
extern "C" {
#endif
#define BKTR_NODE_HEADER_SIZE 0x10
#define BKTR_NODE_SIZE 0x4000 /* Currently shared by all Bucket Tree storage types. */
#define BKTR_NODE_SIZE_MIN 0x400
#define BKTR_NODE_SIZE_MAX 0x80000
#define BKTR_INDIRECT_ENTRY_SIZE 0x14
#define BKTR_AES_CTR_EX_ENTRY_SIZE 0x10
#define BKTR_COMPRESSED_ENTRY_SIZE 0x18
#define BKTR_COMPRESSION_PHYS_ALIGNMENT 0x10
#define BKTR_COMPRESSION_LEVEL_MIN 0
#define BKTR_COMPRESSION_LEVEL_MAX 16
#define BKTR_COMPRESSION_LEVEL_DEFAULT BKTR_COMPRESSION_LEVEL_MIN
#define BKTR_COMPRESSION_INVALID_PHYS_SIZE UINT32_MAX
#define BKTR_MAX_SUBSTORAGE_COUNT 2
/// Used as the header for both BucketTreeOffsetNode and BucketTreeEntryNode.
typedef struct {
u32 index; ///< BucketTreeOffsetNode / BucketTreeEntryNode index.
u32 count; ///< BucketTreeOffsetNode: BucketTreeEntryNode count. BucketTreeEntryNode: entry count.
u64 offset; ///< Usually represents a physical or virtual size.
} BucketTreeNodeHeader;
NXDT_ASSERT(BucketTreeNodeHeader, BKTR_NODE_HEADER_SIZE);
/// First segment of every BucketTreeTable.
typedef struct {
BucketTreeNodeHeader header;
u64 offsets[0x7FE]; ///< May represent virtual or physical offsets, depending on the storage type.
} BucketTreeOffsetNode;
NXDT_ASSERT(BucketTreeOffsetNode, BKTR_NODE_SIZE);
/// IndirectStorage-related elements.
typedef enum {
BucketTreeIndirectStorageIndex_Original = 0,
BucketTreeIndirectStorageIndex_Patch = 1,
BucketTreeIndirectStorageIndex_Count = 2 ///< Total values supported by this enum.
} BucketTreeIndirectStorageIndex;
#pragma pack(push, 1)
typedef struct {
u64 virtual_offset;
u64 physical_offset;
u32 storage_index; ///< BucketTreeIndirectStorageIndex.
} BucketTreeIndirectStorageEntry;
#pragma pack(pop)
NXDT_ASSERT(BucketTreeIndirectStorageEntry, BKTR_INDIRECT_ENTRY_SIZE);
/// AesCtrExStorage-related elements.
typedef enum {
BucketTreeAesCtrExStorageEncryption_Enabled = 0,
BucketTreeAesCtrExStorageEncryption_Disabled = 1,
BucketTreeAesCtrExStorageEncryption_Count = 2 ///< Total values supported by this enum.
} BucketTreeAesCtrExStorageEncryption;
typedef struct {
u64 offset;
u8 encryption; ///< BucketTreeAesCtrExStorageEncryption.
u8 reserved[0x3];
u32 generation;
} BucketTreeAesCtrExStorageEntry;
NXDT_ASSERT(BucketTreeAesCtrExStorageEntry, BKTR_AES_CTR_EX_ENTRY_SIZE);
/// CompressedStorage-related elements.
typedef enum {
BucketTreeCompressedStorageCompressionType_None = 0,
BucketTreeCompressedStorageCompressionType_Zero = 1,
BucketTreeCompressedStorageCompressionType_2 = 2,
BucketTreeCompressedStorageCompressionType_LZ4 = 3,
BucketTreeCompressedStorageCompressionType_Count = 4 ///< Total values supported by this enum.
} BucketTreeCompressedStorageCompressionType;
typedef struct {
s64 virtual_offset;
s64 physical_offset; ///< Must be aligned to BKTR_COMPRESSION_PHYS_ALIGNMENT.
u8 compression_type; ///< BucketTreeCompressedStorageCompressionType.
s8 compression_level; ///< Must be within the range [BKTR_COMPRESSION_LEVEL_MIN, BKTR_COMPRESSION_LEVEL_MAX].
u8 reserved[0x2];
u32 physical_size; ///< Compressed data size.
} BucketTreeCompressedStorageEntry;
NXDT_ASSERT(BucketTreeCompressedStorageEntry, BKTR_COMPRESSED_ENTRY_SIZE);
/// Second segment of every BucketTreeTable. At least one entry node must be available.
typedef struct {
BucketTreeNodeHeader header;
union {
struct {
BucketTreeIndirectStorageEntry indirect_entries[0x332];
u8 reserved[0x8];
};
BucketTreeAesCtrExStorageEntry aes_ctr_ex_entries[0x3FF];
BucketTreeCompressedStorageEntry compressed_entries[0x2AA];
};
} BucketTreeEntryNode;
NXDT_ASSERT(BucketTreeEntryNode, BKTR_NODE_SIZE);
typedef struct {
BucketTreeOffsetNode offset_node;
BucketTreeEntryNode entry_nodes[]; ///< Number of nodes can be retrieved from offset_node.header.count.
} BucketTreeTable;
NXDT_ASSERT(BucketTreeTable, BKTR_NODE_SIZE);
typedef enum {
BucketTreeStorageType_Indirect = 0, ///< Uses two substorages: index 0 (points to the base NCA) and index 1 (AesCtrEx storage).
///< All reads within storage index 0 use the calculated physical offsets for data decryption.
BucketTreeStorageType_AesCtrEx = 1, ///< Used as storage index 1 for BucketTreeStorageType_Indirect.
BucketTreeStorageType_Compressed = 2, ///< Uses LZ4-compressed sections. If available, this is always the outmost storage type for any NCA. May be used by all title types.
BucketTreeStorageType_Sparse = 3, ///< BucketTreeStorageType_Indirect with a twist. Storage index 0 points to the same NCA, and uses virtual offsets for data decryption.
///< Zero-filled output is used for any reads within storage index 1.
BucketTreeStorageType_Count = 4 ///< Total values supported by this enum.
} BucketTreeStorageType;
typedef enum {
BucketTreeSubStorageType_Regular = 0, ///< Body storage with None, XTS or CTR crypto. Most common substorage type, used in all title types.
///< May be used as substorage for all other BucketTreeStorage types.
BucketTreeSubStorageType_Indirect = 1, ///< Indirect storage. Only used in patches. May be used as substorage for BucketTreeStorageType_Compressed only.
BucketTreeSubStorageType_AesCtrEx = 2, ///< AesCtrEx storage. Only used in patches. Must be used as substorage #1 for BucketTreeStorageType_Indirect.
BucketTreeSubStorageType_Sparse = 3, ///< Sparse storage with CTR crypto, using virtual offsets as lower CTR IVs. Only used in base applications.
///< May be used as substorage for BucketTreeStorageType_Compressed or BucketTreeStorageType_Indirect (#0).
BucketTreeSubStorageType_Count = 4 ///< Total values supported by this enum.
} BucketTreeSubStorageType;
// Forward declaration for BucketTreeSubStorage.
typedef struct _BucketTreeContext BucketTreeContext;
typedef struct {
u8 index; ///< Substorage index.
NcaFsSectionContext *nca_fs_ctx; ///< NCA FS section context. Used to perform operations on the target NCA.
u8 type; ///< BucketTreeSubStorageType.
BucketTreeContext *bktr_ctx; ///< BucketTreeContext related to this storage. Only used if type > BucketTreeSubStorageType_Regular.
} BucketTreeSubStorage;
struct _BucketTreeContext {
NcaFsSectionContext *nca_fs_ctx; ///< NCA FS section context. Used to perform operations on the target NCA.
u8 storage_type; ///< BucketTreeStorageType.
BucketTreeTable *storage_table; ///< Pointer to the dynamically allocated Bucket Tree Table for this storage.
u64 node_size; ///< Node size for this type of Bucket Tree storage.
u64 entry_size; ///< Size of each individual entry within BucketTreeEntryNode.
u32 offset_count; ///< Number of offsets available within each BucketTreeOffsetNode for this storage.
u32 entry_set_count; ///< Number of BucketTreeEntryNode elements available in this storage.
u64 node_storage_size; ///< Offset node segment size within 'storage_table'.
u64 entry_storage_size; ///< Entry node segment size within 'storage_table'.
u64 start_offset; ///< Virtual storage start offset.
u64 end_offset; ///< Virtual storage end offset.
BucketTreeSubStorage substorages[BKTR_MAX_SUBSTORAGE_COUNT]; ///< Substorages required for this BucketTree storage. May be set after initializing this context.
};
/// Initializes a Bucket Tree context using the provided NCA FS section context and a storage type.
/// 'storage_type' may only be BucketTreeStorageType_Indirect, BucketTreeStorageType_AesCtrEx or BucketTreeStorageType_Sparse.
bool bktrInitializeContext(BucketTreeContext *out, NcaFsSectionContext *nca_fs_ctx, u8 storage_type);
/// Initializes a Bucket Tree context with type BucketTreeStorageType_Compressed using the provided BucketTreeSubStorage.
bool bktrInitializeCompressedStorageContext(BucketTreeContext *out, BucketTreeSubStorage *substorage);
/// Sets a BucketTreeSubStorageType_Regular substorage at index 0 in the provided BucketTreeContext.
/// The storage type from the provided BucketTreeContext may only be BucketTreeStorageType_Indirect, BucketTreeStorageType_AesCtrEx or BucketTreeStorageType_Sparse.
bool bktrSetRegularSubStorage(BucketTreeContext *ctx, NcaFsSectionContext *nca_fs_ctx);
/// Sets a BucketTreeSubStorage at the provided index within the parent BucketTreeContext using a previously initialized child BucketTreeContext.
/// The storage type from the provided parent BucketTreeContext may only be BucketTreeStorageType_Indirect.
/// The storage type from the provided child BucketTreeContext may only be BucketTreeStorageType_AesCtrEx (#1) or BucketTreeStorageType_Sparse (#0).
bool bktrSetBucketTreeSubStorage(BucketTreeContext *parent_ctx, BucketTreeContext *child_ctx, u8 substorage_index);
/// Reads data from a Bucket Tree storage using a previously initialized BucketTreeContext.
bool bktrReadStorage(BucketTreeContext *ctx, void *out, u64 read_size, u64 offset);
/// Checks if the provided block extents are within the provided BucketTreeContext's Indirect Storage.
/// The storage type from the provided BucketTreeContext may only be BucketTreeStorageType_Indirect or BucketTreeStorageType_Compressed (with an underlying Indirect substorage).
bool bktrIsBlockWithinIndirectStorageRange(BucketTreeContext *ctx, u64 offset, u64 size, bool *out);
/// Helper inline functions.
NX_INLINE void bktrFreeContext(BucketTreeContext *ctx)
{
if (!ctx) return;
if (ctx->storage_table) free(ctx->storage_table);
memset(ctx, 0, sizeof(BucketTreeContext));
}
NX_INLINE bool bktrIsValidContext(BucketTreeContext *ctx)
{
return (ctx && ctx->nca_fs_ctx && ctx->storage_type < BucketTreeStorageType_Count && ctx->storage_table && ctx->node_size && ctx->entry_size && ctx->offset_count && \
ctx->entry_set_count && ctx->node_storage_size && ctx->entry_storage_size && ctx->end_offset > ctx->start_offset);
}
NX_INLINE bool bktrIsOffsetWithinStorageRange(BucketTreeContext *ctx, u64 offset)
{
return (bktrIsValidContext(ctx) && ctx->start_offset <= offset && offset < ctx->end_offset);
}
NX_INLINE bool bktrIsBlockWithinStorageRange(BucketTreeContext *ctx, u64 size, u64 offset)
{
return (bktrIsValidContext(ctx) && size > 0 && ctx->start_offset <= offset && size <= (ctx->end_offset - offset));
}
NX_INLINE bool bktrIsValidSubStorage(BucketTreeSubStorage *substorage)
{
return (substorage && substorage->index < BKTR_MAX_SUBSTORAGE_COUNT && substorage->nca_fs_ctx && substorage->type < BucketTreeSubStorageType_Count && \
((substorage->type == BucketTreeSubStorageType_Regular && substorage->index == 0 && !substorage->bktr_ctx) || \
(substorage->type > BucketTreeSubStorageType_Regular && substorage->bktr_ctx && substorage->bktr_ctx->nca_fs_ctx == substorage->nca_fs_ctx)));
}
#ifdef __cplusplus
}
#endif
#endif /* __BKTR_H__ */

291
include/core/cert.h Normal file
View File

@ -0,0 +1,291 @@
/*
* cert.h
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __CERT_H__
#define __CERT_H__
#include "signature.h"
#ifdef __cplusplus
extern "C" {
#endif
#define SIGNED_CERT_MIN_SIZE sizeof(CertSigHmac160PubKeyEcc480)
#define SIGNED_CERT_MAX_SIZE sizeof(CertSigRsa4096PubKeyRsa4096)
#define CERT_RSA_PUB_EXP_SIZE 4
#define GENERATE_CERT_STRUCT(sigtype, pubkeytype, certsize) \
typedef struct { \
SignatureBlock##sigtype sig_block; \
CertCommonBlock cert_common_block; \
CertPublicKeyBlock##pubkeytype pub_key_block; \
} CertSig##sigtype##PubKey##pubkeytype; \
NXDT_ASSERT(CertSig##sigtype##PubKey##pubkeytype, certsize);
typedef enum {
CertPubKeyType_Rsa4096 = 0,
CertPubKeyType_Rsa2048 = 1,
CertPubKeyType_Ecc480 = 2,
CertPubKeyType_Count = 3 ///< Total values supported by this enum.
} CertPubKeyType;
/// Placed after the certificate signature block.
typedef struct {
char issuer[0x40];
u32 pub_key_type; ///< CertPubKeyType. Always stored using big endian byte order.
char name[0x40];
u32 date; ///< Stored using big endian byte order.
} CertCommonBlock;
NXDT_ASSERT(CertCommonBlock, 0x88);
/// RSA-4096 public key block. Placed after the certificate common block.
typedef struct {
u8 public_key[0x200];
u8 public_exponent[CERT_RSA_PUB_EXP_SIZE];
u8 padding[0x34];
} CertPublicKeyBlockRsa4096;
NXDT_ASSERT(CertPublicKeyBlockRsa4096, 0x238);
/// RSA-2048 public key block. Placed after the certificate common block.
typedef struct {
u8 public_key[0x100];
u8 public_exponent[CERT_RSA_PUB_EXP_SIZE];
u8 padding[0x34];
} CertPublicKeyBlockRsa2048;
NXDT_ASSERT(CertPublicKeyBlockRsa2048, 0x138);
/// ECC public key block. Placed after the certificate common block.
typedef struct {
u8 public_key[0x3C];
u8 padding[0x3C];
} CertPublicKeyBlockEcc480;
NXDT_ASSERT(CertPublicKeyBlockEcc480, 0x78);
/// All certificates generated below use a big endian sig_type field.
/// Certificates with RSA-4096 signatures.
GENERATE_CERT_STRUCT(Rsa4096, Rsa4096, 0x500); /// pub_key_type field must be CertPubKeyType_Rsa4096.
GENERATE_CERT_STRUCT(Rsa4096, Rsa2048, 0x400); /// pub_key_type field must be CertPubKeyType_Rsa2048.
GENERATE_CERT_STRUCT(Rsa4096, Ecc480, 0x340); /// pub_key_type field must be CertPubKeyType_Ecc480.
/// Certificates with RSA-2048 signatures.
GENERATE_CERT_STRUCT(Rsa2048, Rsa4096, 0x400); /// pub_key_type field must be CertPubKeyType_Rsa4096.
GENERATE_CERT_STRUCT(Rsa2048, Rsa2048, 0x300); /// pub_key_type field must be CertPubKeyType_Rsa2048.
GENERATE_CERT_STRUCT(Rsa2048, Ecc480, 0x240); /// pub_key_type field must be CertPubKeyType_Ecc480.
/// Certificates with ECC signatures.
GENERATE_CERT_STRUCT(Ecc480, Rsa4096, 0x340); /// pub_key_type field must be CertPubKeyType_Rsa4096.
GENERATE_CERT_STRUCT(Ecc480, Rsa2048, 0x240); /// pub_key_type field must be CertPubKeyType_Rsa2048.
GENERATE_CERT_STRUCT(Ecc480, Ecc480, 0x180); /// pub_key_type field must be CertPubKeyType_Ecc480.
/// Certificates with HMAC signatures.
GENERATE_CERT_STRUCT(Hmac160, Rsa4096, 0x300); /// pub_key_type field must be CertPubKeyType_Rsa4096.
GENERATE_CERT_STRUCT(Hmac160, Rsa2048, 0x200); /// pub_key_type field must be CertPubKeyType_Rsa2048.
GENERATE_CERT_STRUCT(Hmac160, Ecc480, 0x140); /// pub_key_type field must be CertPubKeyType_Ecc480.
/// Certificate type.
typedef enum {
CertType_None = 0,
CertType_SigRsa4096_PubKeyRsa4096 = 1,
CertType_SigRsa4096_PubKeyRsa2048 = 2,
CertType_SigRsa4096_PubKeyEcc480 = 3,
CertType_SigRsa2048_PubKeyRsa4096 = 4,
CertType_SigRsa2048_PubKeyRsa2048 = 5,
CertType_SigRsa2048_PubKeyEcc480 = 6,
CertType_SigEcc480_PubKeyRsa4096 = 7,
CertType_SigEcc480_PubKeyRsa2048 = 8,
CertType_SigEcc480_PubKeyEcc480 = 9,
CertType_SigHmac160_PubKeyRsa4096 = 10,
CertType_SigHmac160_PubKeyRsa2048 = 11,
CertType_SigHmac160_PubKeyEcc480 = 12,
CertType_Count = 13 ///< Total values supported by this enum.
} CertType;
/// Used to store certificate type, size and raw data.
typedef struct {
u8 type; ///< CertType.
u64 size; ///< Raw certificate size.
u8 data[SIGNED_CERT_MAX_SIZE]; ///< Raw certificate data.
} Certificate;
/// Used to store multiple certificates.
typedef struct {
u32 count; ///< Number of certificates in this chain.
u64 size; ///< Raw certificate chain size (when concatenated).
Certificate *certs; ///< Certificate array.
} CertificateChain;
/// Retrieves a certificate by its name (e.g. "CA00000003", "XS00000020", etc.).
bool certRetrieveCertificateByName(Certificate *dst, const char *name);
/// Retrieves a certificate chain by a full signature issuer string (e.g. "Root-CA00000003-XS00000020").
bool certRetrieveCertificateChainBySignatureIssuer(CertificateChain *dst, const char *issuer);
/// Returns a pointer to a dynamically allocated buffer that holds the concatenated raw contents from the certificate chain matching the input signature issuer.
/// The returned buffer must be freed by the user.
/// Returns NULL if an error occurs.
u8 *certGenerateRawCertificateChainBySignatureIssuer(const char *issuer, u64 *out_size);
/// Returns a pointer to a dynamically allocated buffer that holds the concatenated raw contents from the certificate chain matching the input rights ID in the inserted gamecard.
/// The returned buffer must be freed by the user.
/// Returns NULL if an error occurs.
u8 *certRetrieveRawCertificateChainFromGameCardByRightsId(const FsRightsId *id, u64 *out_size);
/// General purpose helper inline functions.
NX_INLINE bool certIsValidPublicKeyType(u32 type)
{
return (type < CertPubKeyType_Count);
}
NX_INLINE u64 certGetPublicKeySizeByType(u32 type)
{
return (u64)(type == CertPubKeyType_Rsa4096 ? MEMBER_SIZE(CertPublicKeyBlockRsa4096, public_key) : \
(type == CertPubKeyType_Rsa2048 ? MEMBER_SIZE(CertPublicKeyBlockRsa2048, public_key) : \
(type == CertPubKeyType_Ecc480 ? MEMBER_SIZE(CertPublicKeyBlockEcc480, public_key) : 0)));
}
NX_INLINE u64 certGetPublicKeyBlockSizeByType(u32 type)
{
return (u64)(type == CertPubKeyType_Rsa4096 ? sizeof(CertPublicKeyBlockRsa4096) : \
(type == CertPubKeyType_Rsa2048 ? sizeof(CertPublicKeyBlockRsa2048) : \
(type == CertPubKeyType_Ecc480 ? sizeof(CertPublicKeyBlockEcc480) : 0)));
}
NX_INLINE u32 certGetPublicKeyTypeFromCommonBlock(CertCommonBlock *cert_common_block)
{
return (cert_common_block ? __builtin_bswap32(cert_common_block->pub_key_type) : CertPubKeyType_Count);
}
/// Helper inline functions for signed certificate blobs.
NX_INLINE CertCommonBlock *certGetCommonBlockFromSignedCertBlob(void *buf)
{
return (CertCommonBlock*)signatureGetPayloadFromSignedBlob(buf, true);
}
NX_INLINE bool certIsValidSignedCertBlob(void *buf)
{
CertCommonBlock *cert_common_block = certGetCommonBlockFromSignedCertBlob(buf);
return (cert_common_block && certIsValidPublicKeyType(certGetPublicKeyTypeFromCommonBlock(cert_common_block)));
}
NX_INLINE u32 certGetPublicKeyTypeFromSignedCertBlob(void *buf)
{
return (certIsValidSignedCertBlob(buf) ? certGetPublicKeyTypeFromCommonBlock(certGetCommonBlockFromSignedCertBlob(buf)) : CertPubKeyType_Count);
}
NX_INLINE u64 certGetPublicKeySizeFromSignedCertBlob(void *buf)
{
return certGetPublicKeySizeByType(certGetPublicKeyTypeFromSignedCertBlob(buf));
}
NX_INLINE u64 certGetPublicKeyBlockSizeFromSignedCertBlob(void *buf)
{
return certGetPublicKeyBlockSizeByType(certGetPublicKeyTypeFromSignedCertBlob(buf));
}
NX_INLINE u8 *certGetPublicKeyFromSignedCertBlob(void *buf)
{
return (certIsValidSignedCertBlob(buf) ? ((u8*)certGetCommonBlockFromSignedCertBlob(buf) + sizeof(CertCommonBlock)) : NULL);
}
NX_INLINE u8 *certGetPublicExponentFromSignedCertBlob(void *buf)
{
u32 pub_key_type = certGetPublicKeyTypeFromSignedCertBlob(buf);
u8 *public_key = certGetPublicKeyFromSignedCertBlob(buf);
return (pub_key_type < CertPubKeyType_Ecc480 ? (public_key + certGetPublicKeySizeByType(pub_key_type)) : NULL); // Only allow RSA public key types.
}
NX_INLINE u64 certGetSignedCertBlobSize(void *buf)
{
return (certIsValidSignedCertBlob(buf) ? (signatureGetBlockSizeFromSignedBlob(buf, true) + sizeof(CertCommonBlock) + certGetPublicKeyBlockSizeFromSignedCertBlob(buf)) : 0);
}
NX_INLINE u64 certGetSignedCertBlobHashAreaSize(void *buf)
{
return (certIsValidSignedCertBlob(buf) ? (sizeof(CertCommonBlock) + certGetPublicKeyBlockSizeFromSignedCertBlob(buf)) : 0);
}
/// Helper inline functions for Certificate elements.
NX_INLINE bool certIsValidCertificate(Certificate *cert)
{
return (cert && cert->type > CertType_None && cert->type < CertType_Count && cert->size >= SIGNED_CERT_MIN_SIZE && cert->size <= SIGNED_CERT_MAX_SIZE && \
certIsValidSignedCertBlob(cert->data));
}
NX_INLINE CertCommonBlock *certGetCommonBlockFromCertificate(Certificate *cert)
{
return (certIsValidCertificate(cert) ? certGetCommonBlockFromSignedCertBlob(cert->data) : NULL);
}
NX_INLINE u32 certGetPublicKeyTypeFromCertificate(Certificate *cert)
{
return (certIsValidCertificate(cert) ? certGetPublicKeyTypeFromSignedCertBlob(cert->data) : CertPubKeyType_Count);
}
NX_INLINE u64 certGetPublicKeySizeFromCertificate(Certificate *cert)
{
return (certIsValidCertificate(cert) ? certGetPublicKeySizeFromSignedCertBlob(cert->data) : 0);
}
NX_INLINE u64 certGetPublicKeyBlockSizeFromCertificate(Certificate *cert)
{
return (certIsValidCertificate(cert) ? certGetPublicKeyBlockSizeFromSignedCertBlob(cert->data) : 0);
}
NX_INLINE u8 *certGetPublicKeyFromCertificate(Certificate *cert)
{
return (certIsValidCertificate(cert) ? certGetPublicKeyFromSignedCertBlob(cert->data) : NULL);
}
NX_INLINE u8 *certGetPublicExponentFromCertificate(Certificate *cert)
{
return (certIsValidCertificate(cert) ? certGetPublicExponentFromSignedCertBlob(cert->data) : NULL);
}
NX_INLINE u64 certGetHashAreaSizeFromCertificate(Certificate *cert)
{
return (certIsValidCertificate(cert) ? certGetSignedCertBlobHashAreaSize(cert->data) : 0);
}
/// Helper inline functions for CertificateChain elements.
NX_INLINE void certFreeCertificateChain(CertificateChain *chain)
{
if (!chain) return;
if (chain->certs) free(chain->certs);
memset(chain, 0, sizeof(CertificateChain));
}
#ifdef __cplusplus
}
#endif
#endif /* __CERT_H__ */

398
include/core/cnmt.h Normal file
View File

@ -0,0 +1,398 @@
/*
* cnmt.h
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __CNMT_H__
#define __CNMT_H__
#include "pfs.h"
#ifdef __cplusplus
extern "C" {
#endif
#define CNMT_DIGEST_SIZE SHA256_HASH_SIZE
/// Equivalent to NcmContentMetaAttribute.
typedef enum {
ContentMetaAttribute_None = 0,
ContentMetaAttribute_IncludesExFatDriver = BIT(0),
ContentMetaAttribute_Rebootless = BIT(1),
ContentMetaAttribute_Compacted = BIT(2), ///< One or more NCAs use SparseInfo data.
ContentMetaAttribute_Count = 3 ///< Total values supported by this enum.
} ContentMetaAttribute;
typedef enum {
ContentMetaPlatform_Nx = 0,
ContentMetaPlatform_Count = 1 ///< Total values supported by this enum.
} ContentMetaPlatform;
typedef enum {
ContentMetaInstallState_None = 0,
ContentMetaInstallState_Committed = BIT(0),
ContentMetaInstallState_Count = 1 ///< Total values supported by this enum.
} ContentMetaInstallState;
/// Extended variation of NcmContentMetaHeader. This is essentially the start of every CNMT file.
/// Depending on the content meta type value, this header may be followed by an additional extended header for that specific content meta type.
/// NcmPackagedContentInfo and/or NcmContentMetaInfo entries may follow afterwards.
/// If the extended data size field from the extended header (if available) is non-zero, this data is saved after the content info entries.
/// Finally, a 0x20 byte long digest is appended to the EOF.
typedef struct {
u64 title_id;
Version version;
u8 content_meta_type; ///< NcmContentMetaType.
u8 content_meta_platform; ///< ContentMetaPlatform.
u16 extended_header_size; ///< Must match the size from the extended header struct for this content meta type (SystemUpdate, Application, Patch, AddOnContent, Delta).
u16 content_count; ///< Determines how many NcmPackagedContentInfo entries are available after the extended header.
u16 content_meta_count; ///< Determines how many NcmContentMetaInfo entries are available after the NcmPackagedContentInfo entries. Only used for SystemUpdate.
u8 content_meta_attribute; ///< ContentMetaAttribute.
u8 storage_id; ///< NcmStorageId.
u8 content_install_type; ///< NcmContentInstallType.
u8 install_state; ///< ContentMetaInstallState.
Version required_download_system_version;
u8 reserved[0x4];
} ContentMetaPackagedContentMetaHeader;
NXDT_ASSERT(ContentMetaPackagedContentMetaHeader, 0x20);
/// Extended header for the SystemUpdate title.
/// Equivalent to NcmSystemUpdateMetaExtendedHeader.
typedef struct {
u32 extended_data_size;
} ContentMetaSystemUpdateMetaExtendedHeader;
NXDT_ASSERT(ContentMetaSystemUpdateMetaExtendedHeader, 0x4);
/// Extended header for Application titles.
/// Equivalent to NcmApplicationMetaExtendedHeader, but using Version structs.
typedef struct {
u64 patch_id;
Version required_system_version;
Version required_application_version;
} ContentMetaApplicationMetaExtendedHeader;
NXDT_ASSERT(ContentMetaApplicationMetaExtendedHeader, 0x10);
/// Extended header for Patch titles.
/// Equivalent to NcmPatchMetaExtendedHeader, but using a Version struct.
typedef struct {
u64 application_id;
Version required_system_version;
u32 extended_data_size;
u8 reserved[0x8];
} ContentMetaPatchMetaExtendedHeader;
NXDT_ASSERT(ContentMetaPatchMetaExtendedHeader, 0x18);
typedef enum {
ContentMetaContentAccessibility_None = 0,
ContentMetaContentAccessibility_Individual = BIT(0),
ContentMetaContentAccessibility_Count = 1 ///< Total values supported by this enum.
} ContentMetaContentAccessibility;
/// Extended header for AddOnContent tiles (15.0.0+).
/// Equivalent to NcmAddOnContentMetaExtendedHeader, but using a Version struct.
typedef struct {
u64 application_id;
Version required_application_version;
u8 content_accessibility; ///< ContentMetaContentAccessibility.
u8 reserved[0x3];
u64 data_patch_id;
} ContentMetaAddOnContentMetaExtendedHeader;
NXDT_ASSERT(ContentMetaAddOnContentMetaExtendedHeader, 0x18);
/// Old extended header for AddOnContent titles (1.0.0 - 14.1.2).
/// Equivalent to NcmLegacyAddOnContentMetaExtendedHeader, but using a Version struct.
typedef struct {
u64 application_id;
Version required_application_version;
u8 reserved[0x4];
} ContentMetaLegacyAddOnContentMetaExtendedHeader;
NXDT_ASSERT(ContentMetaLegacyAddOnContentMetaExtendedHeader, 0x10);
/// Extended header for Delta titles.
typedef struct {
u64 application_id;
u32 extended_data_size;
u8 reserved[0x4];
} ContentMetaDeltaMetaExtendedHeader;
NXDT_ASSERT(ContentMetaDeltaMetaExtendedHeader, 0x10);
/// Extended header for DataPatch titles.
/// Equivalent to NcmDataPatchMetaExtendedHeader, but using a Version struct.
typedef struct {
u64 data_id;
u64 application_id;
Version required_application_version;
u32 extended_data_size;
u8 reserved[0x8];
} ContentMetaDataPatchMetaExtendedHeader;
NXDT_ASSERT(ContentMetaDataPatchMetaExtendedHeader, 0x20);
typedef enum {
ContentMetaFirmwareVariationVersion_Invalid = 0,
ContentMetaFirmwareVariationVersion_V1 = 1,
ContentMetaFirmwareVariationVersion_V2 = 2,
ContentMetaFirmwareVariationVersion_Unknown = 3,
ContentMetaFirmwareVariationVersion_Count = 4 ///< Total values supported by this enum.
} ContentMetaFirmwareVariationVersion;
/// Header for the extended data region in the SystemUpdate title, pointed to by the extended header.
/// If version is ContentMetaFirmwareVariationVersion_V1, this is followed by 'variation_count' ContentMetaFirmwareVariationInfoV1 entries.
/// Otherwise, if version is ContentMetaFirmwareVariationVersion_V2, this is followed by:
/// * 'variation_count' firmware variation IDs (4 bytes each).
/// * 'variation_count' ContentMetaFirmwareVariationInfoV2 entries.
/// * (Optionally) A variable number of NcmContentMetaInfo entries, which is the sum of all 'meta_count' values from ContentMetaFirmwareVariationInfoV2 entries where 'refer_to_base' is set to false.
typedef struct {
u32 version; ///< ContentMetaFirmwareVariationVersion.
u32 variation_count; ///< Determines how many firmware variation entries are available after this header.
} ContentMetaSystemUpdateMetaExtendedDataHeader;
NXDT_ASSERT(ContentMetaSystemUpdateMetaExtendedDataHeader, 0x8);
/// Used if the firmware variation version matches ContentMetaFirmwareVariationVersion_V1.
typedef struct {
u32 firmware_variation_id;
u8 reserved[0x1C];
} ContentMetaFirmwareVariationInfoV1;
NXDT_ASSERT(ContentMetaFirmwareVariationInfoV1, 0x20);
/// Used if the firmware variation version matches ContentMetaFirmwareVariationVersion_V2.
typedef struct {
bool refer_to_base;
u8 reserved_1[0x3];
u32 meta_count;
u8 reserved_2[0x18];
} ContentMetaFirmwareVariationInfoV2;
NXDT_ASSERT(ContentMetaFirmwareVariationInfoV2, 0x20);
/// Header for the extended data region in Patch titles, pointed to by the extended header.
/// This is followed by:
/// * 'history_count' ContentMetaPatchHistoryHeader entries.
/// * 'delta_history_count' ContentMetaPatchDeltaHistory entries.
/// * 'delta_count' ContentMetaPatchDeltaHeader entries.
/// * 'fragment_set_count' ContentMetaFragmentSet entries.
/// * 'history_content_count' NcmContentInfo entries.
/// * 'delta_content_count' NcmPackagedContentInfo entries.
/// * A variable number of ContentMetaFragmentIndicator entries, which is the sum of all 'fragment_count' values from ContentMetaFragmentSet entries.
typedef struct {
u32 history_count;
u32 delta_history_count;
u32 delta_count;
u32 fragment_set_count;
u32 history_content_count;
u32 delta_content_count;
u8 reserved[0x4];
} ContentMetaPatchMetaExtendedDataHeader;
NXDT_ASSERT(ContentMetaPatchMetaExtendedDataHeader, 0x1C);
typedef struct {
NcmContentMetaKey content_meta_key;
u8 digest[CNMT_DIGEST_SIZE];
u16 content_info_count;
u8 reserved[0x6];
} ContentMetaPatchHistoryHeader;
NXDT_ASSERT(ContentMetaPatchHistoryHeader, 0x38);
typedef struct {
u64 source_patch_id;
u64 destination_patch_id;
Version source_version;
Version destination_version;
u64 download_size;
u8 reserved[0x8];
} ContentMetaPatchDeltaHistory;
NXDT_ASSERT(ContentMetaPatchDeltaHistory, 0x28);
typedef struct {
u64 source_patch_id;
u64 destination_patch_id;
Version source_version;
Version destination_version;
u16 fragment_set_count;
u8 reserved_1[0x6];
u16 content_info_count;
u8 reserved_2[0x6];
} ContentMetaPatchDeltaHeader;
NXDT_ASSERT(ContentMetaPatchDeltaHeader, 0x28);
typedef enum {
ContentMetaUpdateType_ApplyAsDelta = 0,
ContentMetaUpdateType_Overwrite = 1,
ContentMetaUpdateType_Create = 2,
ContentMetaUpdateType_Count = 3 ///< Total values supported by this enum.
} ContentMetaUpdateType;
#pragma pack(push, 1)
typedef struct {
NcmContentId source_content_id;
NcmContentId destination_content_id;
u32 source_size_low;
u16 source_size_high;
u16 destination_size_high;
u32 destination_size_low;
u16 fragment_count;
u8 fragment_target_content_type; ///< NcmContentType.
u8 update_type; ///< ContentMetaUpdateType.
u8 reserved[0x4];
} ContentMetaFragmentSet;
#pragma pack(pop)
NXDT_ASSERT(ContentMetaFragmentSet, 0x34);
typedef struct {
u16 content_info_index;
u16 fragment_index;
} ContentMetaFragmentIndicator;
NXDT_ASSERT(ContentMetaFragmentIndicator, 0x4);
/// Header for the extended data region in Delta titles, pointed to by the extended header.
/// This is followed by:
/// * 'fragment_set_count' ContentMetaFragmentSet entries.
/// * A variable number of ContentMetaFragmentIndicator entries, which is the sum of all 'fragment_count' values from ContentMetaFragmentSet entries.
typedef struct {
u64 source_patch_id;
u64 destination_patch_id;
Version source_version;
Version destination_version;
u16 fragment_set_count;
u8 reserved[0x6];
} ContentMetaDeltaMetaExtendedDataHeader;
NXDT_ASSERT(ContentMetaDeltaMetaExtendedDataHeader, 0x20);
typedef struct {
NcaContext *nca_ctx; ///< Pointer to the NCA context for the Meta NCA from which CNMT data is retrieved.
PartitionFileSystemContext pfs_ctx; ///< PartitionFileSystemContext for the Meta NCA FS section #0, which is where the CNMT is stored.
PartitionFileSystemEntry *pfs_entry; ///< PartitionFileSystemEntry for the CNMT in the Meta NCA FS section #0. Used to generate a NcaHierarchicalSha256Patch if needed.
NcaHierarchicalSha256Patch nca_patch; ///< NcaHierarchicalSha256Patch generated if CNMT modifications are needed. Used to seamlessly replace Meta NCA data while writing it.
///< Bear in mind that generating a patch modifies the NCA context.
char *cnmt_filename; ///< Pointer to the CNMT filename in the Meta NCA FS section #0.
u8 *raw_data; ///< Pointer to a dynamically allocated buffer that holds the raw CNMT.
u64 raw_data_size; ///< Raw CNMT size. Kept here for convenience - this is part of 'pfs_entry'.
u8 raw_data_hash[SHA256_HASH_SIZE]; ///< SHA-256 checksum calculated over the whole raw CNMT. Used to determine if NcaHierarchicalSha256Patch generation is truly needed.
ContentMetaPackagedContentMetaHeader *packaged_header; ///< Pointer to the ContentMetaPackagedContentMetaHeader within 'raw_data'.
u8 *extended_header; ///< Pointer to the extended header within 'raw_data', if available. May be casted to other types. Its size is stored in 'packaged_header'.
NcmPackagedContentInfo *packaged_content_info; ///< Pointer to the NcmPackagedContentInfo entries within 'raw_data', if available. The content count is stored in 'packaged_header'.
NcmContentMetaInfo *content_meta_info; ///< Pointer to the NcmContentMetaInfo entries within 'raw_data', if available. The content meta count is stored in 'packaged_header'.
u8 *extended_data; ///< Pointer to the extended data block within 'raw_data', if available.
u32 extended_data_size; ///< Size of the extended data block within 'raw_data', if available. Kept here for convenience - this is part of the header in 'extended_data'.
u8 *digest; ///< Pointer to the digest within 'raw_data'.
char *authoring_tool_xml; ///< Pointer to a dynamically allocated, NULL-terminated buffer that holds AuthoringTool-like XML data.
///< This is always NULL unless cnmtGenerateAuthoringToolXml() is used on this ContentMetaContext.
u64 authoring_tool_xml_size; ///< Size for the AuthoringTool-like XML. This is essentially the same as using strlen() on 'authoring_tool_xml'.
///< This is always 0 unless cnmtGenerateAuthoringToolXml() is used on this ContentMetaContext.
} ContentMetaContext;
/// Initializes a ContentMetaContext using a previously initialized NcaContext (which must belong to a Meta NCA).
bool cnmtInitializeContext(ContentMetaContext *out, NcaContext *nca_ctx);
/// Looks for a NcmPackagedContentInfo entry with a content ID that matches the one from the input NcaContext and verifies its hash.
bool cnmtVerifyContentHash(ContentMetaContext *cnmt_ctx, NcaContext *nca_ctx, const u8 *hash);
/// Updates NcmPackagedContentInfo data for the content entry with size, type and ID offset values that match the ones from the input NcaContext.
bool cnmtUpdateContentInfo(ContentMetaContext *cnmt_ctx, NcaContext *nca_ctx);
/// Generates a Partition FS entry patch for the NcaContext pointed to by the input ContentMetaContext, using its raw CNMT data.
bool cnmtGenerateNcaPatch(ContentMetaContext *cnmt_ctx);
/// Writes data from the Partition FS entry patch in the input ContentMetaContext to the provided buffer.
void cnmtWriteNcaPatch(ContentMetaContext *cnmt_ctx, void *buf, u64 buf_size, u64 buf_offset);
/// Generates an AuthoringTool-like XML using information from a previously initialized ContentMetaContext, as well as a pointer to 'nca_ctx_count' NcaContext with content information.
/// If the function succeeds, XML data and size will get saved to the 'authoring_tool_xml' and 'authoring_tool_xml_size' members from the ContentMetaContext.
bool cnmtGenerateAuthoringToolXml(ContentMetaContext *cnmt_ctx, NcaContext *nca_ctx, u32 nca_ctx_count);
/// Helper inline functions.
NX_INLINE void cnmtFreeContext(ContentMetaContext *cnmt_ctx)
{
if (!cnmt_ctx) return;
pfsFreeContext(&(cnmt_ctx->pfs_ctx));
pfsFreeEntryPatch(&(cnmt_ctx->nca_patch));
if (cnmt_ctx->raw_data) free(cnmt_ctx->raw_data);
if (cnmt_ctx->authoring_tool_xml) free(cnmt_ctx->authoring_tool_xml);
memset(cnmt_ctx, 0, sizeof(ContentMetaContext));
}
NX_INLINE bool cnmtIsValidContext(ContentMetaContext *cnmt_ctx)
{
return (cnmt_ctx && cnmt_ctx->nca_ctx && cnmt_ctx->pfs_entry && cnmt_ctx->cnmt_filename && cnmt_ctx->raw_data && cnmt_ctx->raw_data_size && cnmt_ctx->packaged_header && \
((cnmt_ctx->packaged_header->extended_header_size && cnmt_ctx->extended_header) || (!cnmt_ctx->packaged_header->extended_header_size && !cnmt_ctx->extended_header)) && \
((cnmt_ctx->packaged_header->content_count && cnmt_ctx->packaged_content_info) || (!cnmt_ctx->packaged_header->content_count && !cnmt_ctx->packaged_content_info)) && \
((cnmt_ctx->packaged_header->content_meta_count && cnmt_ctx->content_meta_info) || (!cnmt_ctx->packaged_header->content_meta_count && !cnmt_ctx->content_meta_info)) && \
((cnmt_ctx->extended_data_size && cnmt_ctx->extended_data) || (!cnmt_ctx->extended_data_size && !cnmt_ctx->extended_data)) && cnmt_ctx->digest);
}
NX_INLINE u64 cnmtGetRequiredTitleId(ContentMetaContext *cnmt_ctx)
{
if (!cnmtIsValidContext(cnmt_ctx)) return 0;
u8 content_meta_type = cnmt_ctx->packaged_header->content_meta_type;
if (content_meta_type == NcmContentMetaType_Application || content_meta_type == NcmContentMetaType_Patch || content_meta_type == NcmContentMetaType_AddOnContent)
{
return *((u64*)cnmt_ctx->extended_header);
} else
if (content_meta_type == NcmContentMetaType_DataPatch)
{
return ((ContentMetaDataPatchMetaExtendedHeader*)cnmt_ctx->extended_header)->application_id;
}
return 0;
}
NX_INLINE u32 cnmtGetRequiredTitleVersion(ContentMetaContext *cnmt_ctx)
{
if (!cnmtIsValidContext(cnmt_ctx)) return 0;
u8 content_meta_type = cnmt_ctx->packaged_header->content_meta_type;
if (content_meta_type == NcmContentMetaType_Application || content_meta_type == NcmContentMetaType_Patch || content_meta_type == NcmContentMetaType_AddOnContent)
{
return ((Version*)(cnmt_ctx->extended_header + sizeof(u64)))->value;
} else
if (content_meta_type == NcmContentMetaType_DataPatch)
{
return ((ContentMetaDataPatchMetaExtendedHeader*)cnmt_ctx->extended_header)->required_application_version.value;
}
return 0;
}
#ifdef __cplusplus
}
#endif
#endif /* __CNMT_H__ */

61
include/core/config.h Normal file
View File

@ -0,0 +1,61 @@
/*
* config.h
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __CONFIG_H__
#define __CONFIG_H__
#include "nxdt_json.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
ConfigOutputStorage_SdCard = 0,
ConfigOutputStorage_UsbHost = 1,
ConfigOutputStorage_Count = 2 ///< Total values supported by this enum.
} ConfigOutputStorage;
/// Initializes the configuration interface.
bool configInitialize(void);
/// Closes the configuration interface.
void configExit(void);
/// Resets settings to their default values.
void configResetSettings(void);
/// Getters and setters for various data types.
/// Path elements must be separated using forward slashes.
bool configGetBoolean(const char *path);
void configSetBoolean(const char *path, bool value);
int configGetInteger(const char *path);
void configSetInteger(const char *path, int value);
#ifdef __cplusplus
}
#endif
#endif /* __CONFIG_H__ */

View File

@ -0,0 +1,102 @@
/*
* nxdt_devoptab.h
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __NXDT_DEVOPTAB_H__
#define __NXDT_DEVOPTAB_H__
#include "../pfs.h"
#include "../hfs.h"
#include "../romfs.h"
#ifdef __cplusplus
extern "C" {
#endif
#define DEVOPTAB_MOUNT_NAME_LENGTH 32 // Including NULL terminator.
#define DEVOPTAB_DECL_ERROR_STATE int _errno = 0
#define DEVOPTAB_DECL_DEV_CTX DevoptabDeviceContext *dev_ctx = (DevoptabDeviceContext*)r->deviceData
#define DEVOPTAB_DECL_FS_CTX(type) type *fs_ctx = (type*)dev_ctx->fs_ctx
#define DEVOPTAB_DECL_FILE_STATE(type) type *file = (type*)fd
#define DEVOPTAB_DECL_DIR_STATE(type) type *dir = (type*)dirState->dirStruct
#define DEVOPTAB_SET_ERROR(x) r->_errno = _errno = (x)
#define DEVOPTAB_IS_ERROR_SET (_errno != 0)
#define DEVOPTAB_EXIT goto end
#define DEVOPTAB_SET_ERROR_AND_EXIT(x) \
do { \
DEVOPTAB_SET_ERROR(x); \
DEVOPTAB_EXIT; \
} while(0)
#define DEVOPTAB_RETURN_INT(x) return (DEVOPTAB_IS_ERROR_SET ? -1 : (x))
#define DEVOPTAB_RETURN_PTR(x) return (DEVOPTAB_IS_ERROR_SET ? NULL : (x))
#define DEVOPTAB_RETURN_BOOL return (DEVOPTAB_IS_ERROR_SET ? false : true)
#define DEVOPTAB_RETURN_UNSUPPORTED_OP r->_errno = ENOSYS; \
return -1;
#define DEVOPTAB_INIT_VARS(type) devoptabControlMutex(true); \
DEVOPTAB_DECL_ERROR_STATE; \
DEVOPTAB_DECL_DEV_CTX; \
if (!dev_ctx->initialized) DEVOPTAB_SET_ERROR_AND_EXIT(ENODEV);
#define DEVOPTAB_INIT_FILE_VARS(fs_type, file_type) DEVOPTAB_INIT_VARS(fs_type); \
DEVOPTAB_DECL_FILE_STATE(file_type)
#define DEVOPTAB_INIT_DIR_VARS(fs_type, dir_type) DEVOPTAB_INIT_VARS(fs_type); \
DEVOPTAB_DECL_DIR_STATE(dir_type)
#define DEVOPTAB_DEINIT_VARS devoptabControlMutex(false)
typedef struct {
bool initialized; ///< Device initialization flag.
char name[DEVOPTAB_MOUNT_NAME_LENGTH]; ///< Mount name string, without a trailing colon (:).
time_t mount_time; ///< Mount time.
devoptab_t device; ///< Devoptab virtual device interface. Provides a way to use libcstd I/O calls on the mounted filesystem.
void *fs_ctx; ///< Pointer to actual type-specific filesystem context (PartitionFileSystemContext, HashFileSystemContext, RomFileSystemContext).
} DevoptabDeviceContext;
/// Mounts a virtual Partition FS device using the provided Partition FS context and a mount name.
bool devoptabMountPartitionFileSystemDevice(PartitionFileSystemContext *pfs_ctx, const char *name);
/// Mounts a virtual Hash FS device using the provided Hash FS context and a mount name.
bool devoptabMountHashFileSystemDevice(HashFileSystemContext *hfs_ctx, const char *name);
/// Mounts a virtual RomFS device using the provided RomFS context and a mount name.
bool devoptabMountRomFileSystemDevice(RomFileSystemContext *romfs_ctx, const char *name);
/// Unmounts a previously mounted virtual device.
void devoptabUnmountDevice(const char *name);
/// Unmounts all previously mounted virtual devices.
void devoptabUnmountAllDevices(void);
/// (Un)locks the devoptab mutex. Used by filesystem-specific devoptab interfaces.
void devoptabControlMutex(bool lock);
#ifdef __cplusplus
}
#endif
#endif /* __NXDT_DEVOPTAB_H__ */

View File

@ -0,0 +1,55 @@
/*
* ro_dev.h
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __RO_DEV__
#define __RO_DEV__
#ifdef __cplusplus
extern "C" {
#endif
/* Bogus functions for devoptab write operations -- all of these set errno to ENOSYS and return -1. */
/* We don't provide support for relative directories, so chdir is discarded as well. */
ssize_t rodev_write(struct _reent *r, void *fd, const char *ptr, size_t len);
int rodev_link(struct _reent *r, const char *existing, const char *newLink);
int rodev_unlink(struct _reent *r, const char *name);
int rodev_chdir(struct _reent *r, const char *name);
int rodev_rename(struct _reent *r, const char *oldName, const char *newName);
int rodev_mkdir(struct _reent *r, const char *path, int mode);
int rodev_ftruncate(struct _reent *r, void *fd, off_t len);
int rodev_fsync(struct _reent *r, void *fd);
int rodev_chmod(struct _reent *r, const char *path, mode_t mode);
int rodev_fchmod(struct _reent *r, void *fd, mode_t mode);
int rodev_rmdir(struct _reent *r, const char *name);
int rodev_utimes(struct _reent *r, const char *filename, const struct timeval times[2]);
long rodev_fpathconf(struct _reent *r, void *fd, int name);
long rodev_pathconf(struct _reent *r, const char *path, int name);
int rodev_symlink(struct _reent *r, const char *target, const char *linkpath);
ssize_t rodev_readlink(struct _reent *r, const char *path, char *buf, size_t bufsiz);
#ifdef __cplusplus
}
#endif
#endif /* __RO_DEV__ */

96
include/core/elf_symbol.h Normal file
View File

@ -0,0 +1,96 @@
/*
* elf_symbol.h
*
* Copyright (c) 1991-2020, Linus Torvalds and Linux Kernel project contributors.
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __ELF_SYMBOL_H__
#define __ELF_SYMBOL_H__
/* Symbol types. */
#define STT_NOTYPE 0 /* The symbol type not defined. */
#define STT_OBJECT 1 /* The symbol is associated with a data object. */
#define STT_FUNC 2 /* The symbol is associated with a function or other executable code. */
#define STT_SECTION 3 /* The symbol is associated with a section. */
#define STT_FILE 4 /* The symbol's name gives the name of the source file associated with the object file. */
#define STT_COMMON 5 /* The symbol labels an uninitialized common block. */
#define STT_TLS 6 /* The symbol specifies a Thread-Local Storage entity. */
#define STT_LOOS 10 /* Reserved for operating system-specific semantics. */
#define STT_HIOS 12 /* Reserved for operating system-specific semantics. */
#define STT_LOPROC 13 /* Reserved for processor-specific semantics. */
#define STT_HIPROC 15 /* Reserved for processor-specific semantics. */
/* Symbol binding attributes. */
#define STB_LOCAL 0 /* Local symbols are not visible outside the object file containing their definition. */
#define STB_GLOBAL 1 /* Global symbols are visible to all object files being combined. */
#define STB_WEAK 2 /* Weak symbols resemble global symbols, but their definitions have lower precedence. */
#define STB_LOOS 10 /* Reserved for operating system-specific semantics. */
#define STB_HIOS 12 /* Reserved for operating system-specific semantics. */
#define STB_LOPROC 13 /* Reserved for processor-specific semantics. */
#define STB_HIPROC 15 /* Reserved for processor-specific semantics. */
/* Symbol visibility. */
#define STV_DEFAULT 0 /* The symbol visibility is specified by the its binding attribute. */
#define STV_INTERNAL 1 /* The meaning of this visibility attribute may be defined by processor supplements to further constrain hidden symbols. */
#define STV_HIDDEN 2 /* The symbol name is not visible to other components. */
#define STV_PROTECTED 3 /* The symbol is visible in other components but not preemptable. */
/* Section header index. */
#define SHN_UNDEF 0 /* The symbol is undefined. */
#define SHN_LORESERVE 0xFF00
#define SHN_LOPROC 0xFF00
#define SHN_HIPROC 0xFF1F
#define SHN_LOOS 0xFF20
#define SHN_LIVEPATCH 0xFF20
#define SHN_HIOS 0xFF3F
#define SHN_ABS 0xFFF1 /* The symbol has an absolute value that will not change because of relocation. */
#define SHN_COMMON 0xFFF2 /* Labels a common block that has not yet been allocated. */
#define SHN_XINDEX 0xFFFF /* The symbol refers to a specific location within a section, but the section header index is too large to be represented directly. */
#define SHN_HIRESERVE 0xFFFF
/* Macros. */
#define ELF_ST_BIND(x) ((x) >> 4) /* Extracts binding value from a st_info field. */
#define ELF_ST_TYPE(x) ((x) & 0xF) /* Extracts type value from a st_info field. */
#define ELF_ST_VISIBILITY(x) ((x) & 0x3) /* Extracts visibility value from a st_other field. */
typedef struct {
u32 st_name; ///< Symbol name offset within dynamic string table.
u32 st_value; ///< Symbol value.
u32 st_size; ///< Symbol size.
u8 st_info; ///< Symbol type (lower nibble) and binding attributes (upper nibble).
u8 st_other; ///< Currently specifies a symbol's visibility (lower 3 bits).
u16 st_shndx; ///< Holds the relevant section header table index.
} Elf32Symbol;
NXDT_ASSERT(Elf32Symbol, 0x10);
typedef struct {
u32 st_name; ///< Symbol name offset within dynamic string table.
u8 st_info; ///< Symbol type (lower nibble) and binding attributes (upper nibble).
u8 st_other; ///< Currently specifies a symbol's visibility (lower 3 bits).
u16 st_shndx; ///< Holds the relevant section header table index.
u64 st_value; ///< Symbol value.
u64 st_size; ///< Symbol size.
} Elf64Symbol;
NXDT_ASSERT(Elf64Symbol, 0x18);
#endif /* __ELF_SYMBOL_H__ */

44
include/core/es.h Normal file
View File

@ -0,0 +1,44 @@
/*
* es.h
*
* Copyright (c) 2018-2020, Addubz.
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __ES_H__
#define __ES_H__
#ifdef __cplusplus
extern "C" {
#endif
Result esInitialize(void);
void esExit(void);
Result esCountCommonTicket(s32 *out_count);
Result esCountPersonalizedTicket(s32 *out_count);
Result esListCommonTicket(s32 *out_entries_written, FsRightsId *out_ids, s32 count);
Result esListPersonalizedTicket(s32 *out_entries_written, FsRightsId *out_ids, s32 count);
#ifdef __cplusplus
}
#endif
#endif /* __ES_H__ */

View File

@ -1,5 +1,5 @@
/*-----------------------------------------------------------------------/
/ Low level disk interface modlue include file (C)ChaN, 2014 /
/ Low level disk interface modlue include file (C)ChaN, 2019 /
/-----------------------------------------------------------------------*/
#ifndef _DISKIO_DEFINED
@ -28,8 +28,8 @@ typedef enum {
DSTATUS disk_initialize (BYTE pdrv);
DSTATUS disk_status (BYTE pdrv);
DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count);
DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count);
DRESULT disk_read (BYTE pdrv, BYTE* buff, LBA_t sector, UINT count);
DRESULT disk_write (BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count);
DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);

View File

@ -1,8 +1,8 @@
/*----------------------------------------------------------------------------/
/ FatFs - Generic FAT Filesystem module R0.13c /
/ FatFs - Generic FAT Filesystem module R0.15 /
/-----------------------------------------------------------------------------/
/
/ Copyright (C) 2018, ChaN, all right reserved.
/ Copyright (C) 2022, ChaN, all right reserved.
/
/ FatFs module is an open source software. Redistribution and use of FatFs in
/ source and binary forms, with or without modification, are permitted provided
@ -20,7 +20,7 @@
#ifndef FF_DEFINED
#define FF_DEFINED 86604 /* Revision ID */
#define FF_DEFINED 80286 /* Revision ID */
#ifdef __cplusplus
extern "C" {
@ -35,51 +35,57 @@ extern "C" {
/* Integer types used for FatFs API */
#if defined(_WIN32) /* Main development platform */
#if defined(_WIN32) /* Windows VC++ (for development only) */
#define FF_INTDEF 2
#include <windows.h>
typedef unsigned __int64 QWORD;
#include <float.h>
#define isnan(v) _isnan(v)
#define isinf(v) (!_finite(v))
#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__cplusplus) /* C99 or later */
#define FF_INTDEF 2
#include <stdint.h>
typedef unsigned int UINT; /* int must be 16-bit or 32-bit */
typedef unsigned char BYTE; /* char must be 8-bit */
typedef uint16_t WORD; /* 16-bit unsigned integer */
typedef uint16_t WCHAR; /* 16-bit unsigned integer */
typedef uint32_t DWORD; /* 32-bit unsigned integer */
typedef uint64_t QWORD; /* 64-bit unsigned integer */
typedef WORD WCHAR; /* UTF-16 character type */
#else /* Earlier than C99 */
#define FF_INTDEF 1
typedef unsigned int UINT; /* int must be 16-bit or 32-bit */
typedef unsigned char BYTE; /* char must be 8-bit */
typedef unsigned short WORD; /* 16-bit unsigned integer */
typedef unsigned short WCHAR; /* 16-bit unsigned integer */
typedef unsigned long DWORD; /* 32-bit unsigned integer */
typedef WORD WCHAR; /* UTF-16 character type */
#endif
/* Definitions of volume management */
/* Type of file size and LBA variables */
#if FF_MULTI_PARTITION /* Multiple partition configuration */
typedef struct {
BYTE pd; /* Physical drive number */
BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */
} PARTITION;
extern PARTITION VolToPart[]; /* Volume - Partition resolution table */
#if FF_FS_EXFAT
#if FF_INTDEF != 2
#error exFAT feature wants C99 or later
#endif
#if FF_STR_VOLUME_ID
#ifndef FF_VOLUME_STRS
extern const char* VolumeStr[FF_VOLUMES]; /* User defied volume ID */
typedef QWORD FSIZE_t;
#if FF_LBA64
typedef QWORD LBA_t;
#else
typedef DWORD LBA_t;
#endif
#else
#if FF_LBA64
#error exFAT needs to be enabled when enable 64-bit LBA
#endif
typedef DWORD FSIZE_t;
typedef DWORD LBA_t;
#endif
/* Type of path name strings on FatFs API */
#ifndef _INC_TCHAR
#define _INC_TCHAR
/* Type of path name strings on FatFs API (TCHAR) */
#if FF_USE_LFN && FF_LFN_UNICODE == 1 /* Unicode in UTF-16 encoding */
typedef WCHAR TCHAR;
@ -101,19 +107,22 @@ typedef char TCHAR;
#define _TEXT(x) x
#endif
/* Definitions of volume management */
#if FF_MULTI_PARTITION /* Multiple partition configuration */
typedef struct {
BYTE pd; /* Physical drive number */
BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */
} PARTITION;
extern PARTITION VolToPart[]; /* Volume - Partition mapping table */
#endif
/* Type of file size variables */
#if FF_FS_EXFAT
#if FF_INTDEF != 2
#error exFAT feature wants C99 or later
#if FF_STR_VOLUME_ID
#ifndef FF_VOLUME_STRS
extern const char* VolumeStr[FF_VOLUMES]; /* User defied volume ID */
#endif
typedef QWORD FSIZE_t;
#else
typedef DWORD FSIZE_t;
#endif
@ -122,10 +131,11 @@ typedef DWORD FSIZE_t;
typedef struct {
BYTE fs_type; /* Filesystem type (0:not mounted) */
BYTE pdrv; /* Associated physical drive */
BYTE pdrv; /* Volume hosting physical drive */
BYTE ldrv; /* Logical drive number (used only when FF_FS_REENTRANT) */
BYTE n_fats; /* Number of FATs (1 or 2) */
BYTE wflag; /* win[] flag (b0:dirty) */
BYTE fsi_flag; /* FSINFO flags (b7:disabled, b0:dirty) */
BYTE wflag; /* win[] status (b0:dirty) */
BYTE fsi_flag; /* FSINFO status (b7:disabled, b0:dirty) */
WORD id; /* Volume mount ID */
WORD n_rootdir; /* Number of root directory entries (FAT12/16) */
WORD csize; /* Cluster size [sectors] */
@ -138,9 +148,6 @@ typedef struct {
#if FF_FS_EXFAT
BYTE* dirbuf; /* Directory entry block scratchpad buffer for exFAT */
#endif
#if FF_FS_REENTRANT
FF_SYNC_t sobj; /* Identifier of sync object */
#endif
#if !FF_FS_READONLY
DWORD last_clst; /* Last allocated cluster */
DWORD free_clst; /* Number of free clusters */
@ -154,15 +161,15 @@ typedef struct {
#endif
#endif
DWORD n_fatent; /* Number of FAT entries (number of clusters + 2) */
DWORD fsize; /* Size of an FAT [sectors] */
DWORD volbase; /* Volume base sector */
DWORD fatbase; /* FAT base sector */
DWORD dirbase; /* Root directory base sector/cluster */
DWORD database; /* Data base sector */
DWORD fsize; /* Number of sectors per FAT */
LBA_t volbase; /* Volume base sector */
LBA_t fatbase; /* FAT base sector */
LBA_t dirbase; /* Root directory base sector (FAT12/16) or cluster (FAT32/exFAT) */
LBA_t database; /* Data base sector */
#if FF_FS_EXFAT
DWORD bitbase; /* Allocation bitmap base sector */
LBA_t bitbase; /* Allocation bitmap base sector */
#endif
DWORD winsect; /* Current sector appearing in the win[] */
LBA_t winsect; /* Current sector appearing in the win[] */
BYTE win[FF_MAX_SS]; /* Disk access window for Directory, FAT (and file data at tiny cfg) */
} FATFS;
@ -172,7 +179,7 @@ typedef struct {
typedef struct {
FATFS* fs; /* Pointer to the hosting volume of this object */
WORD id; /* Hosting volume mount ID */
WORD id; /* Hosting volume's mount ID */
BYTE attr; /* Object attribute */
BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous, =3:fragmented in this session, b2:sub-directory stretched) */
DWORD sclust; /* Object data start cluster (0:no cluster or root directory) */
@ -199,9 +206,9 @@ typedef struct {
BYTE err; /* Abort flag (error code) */
FSIZE_t fptr; /* File read/write pointer (Zeroed on file open) */
DWORD clust; /* Current cluster of fpter (invalid when fptr is 0) */
DWORD sect; /* Sector number appearing in buf[] (0:invalid) */
LBA_t sect; /* Sector number appearing in buf[] (0:invalid) */
#if !FF_FS_READONLY
DWORD dir_sect; /* Sector number containing the directory entry (not used at exFAT) */
LBA_t dir_sect; /* Sector number containing the directory entry (not used at exFAT) */
BYTE* dir_ptr; /* Pointer to the directory entry in the win[] (not used at exFAT) */
#endif
#if FF_USE_FASTSEEK
@ -214,13 +221,13 @@ typedef struct {
/* Directory object structure (DIR) */
/* Directory object structure (FDIR) */
typedef struct {
FFOBJID obj; /* Object identifier */
DWORD dptr; /* Current read/write offset */
DWORD clust; /* Current cluster */
DWORD sect; /* Current sector (0:Read operation has terminated) */
LBA_t sect; /* Current sector (0:Read operation has terminated) */
BYTE* dir; /* Pointer to the directory item in the win[] */
BYTE fn[12]; /* SFN (in/out) {body[8],ext[3],status[1]} */
#if FF_USE_LFN
@ -241,7 +248,7 @@ typedef struct {
WORD ftime; /* Modified time */
BYTE fattrib; /* File attribute */
#if FF_USE_LFN
TCHAR altname[FF_SFN_BUF + 1];/* Altenative file name */
TCHAR altname[FF_SFN_BUF + 1];/* Alternative file name */
TCHAR fname[FF_LFN_BUF + 1]; /* Primary file name */
#else
TCHAR fname[12 + 1]; /* File name */
@ -250,6 +257,18 @@ typedef struct {
/* Format parameter structure (MKFS_PARM) */
typedef struct {
BYTE fmt; /* Format option (FM_FAT, FM_FAT32, FM_EXFAT and FM_SFD) */
BYTE n_fat; /* Number of FATs */
UINT align; /* Data area alignment (sector) */
UINT n_root; /* Number of root directory entries */
DWORD au_size; /* Cluster size (byte) */
} MKFS_PARM;
/* File function return code (FRESULT) */
typedef enum {
@ -277,8 +296,10 @@ typedef enum {
/*--------------------------------------------------------------*/
/* FatFs Module Application Interface */
/*--------------------------------------------------------------*/
/* FatFs module application interface */
FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */
FRESULT f_close (FIL* fp); /* Close an open file object */
@ -305,16 +326,18 @@ FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get numbe
FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */
FRESULT f_setlabel (const TCHAR* label); /* Set volume label */
FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */
FRESULT f_expand (FIL* fp, FSIZE_t szf, BYTE opt); /* Allocate a contiguous block to the file */
FRESULT f_expand (FIL* fp, FSIZE_t fsz, BYTE opt); /* Allocate a contiguous block to the file */
FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */
FRESULT f_mkfs (const TCHAR* path, BYTE opt, DWORD au, void* work, UINT len); /* Create a FAT volume */
FRESULT f_fdisk (BYTE pdrv, const DWORD* szt, void* work); /* Divide a physical drive into some partitions */
FRESULT f_mkfs (const TCHAR* path, const MKFS_PARM* opt, void* work, UINT len); /* Create a FAT volume */
FRESULT f_fdisk (BYTE pdrv, const LBA_t ptbl[], void* work); /* Divide a physical drive into some partitions */
FRESULT f_setcp (WORD cp); /* Set current code page */
int f_putc (TCHAR c, FIL* fp); /* Put a character to the file */
int f_puts (const TCHAR* str, FIL* cp); /* Put a string to the file */
int f_printf (FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */
TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the file */
/* Some API fucntions are implemented as macro */
#define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize))
#define f_error(fp) ((fp)->err)
#define f_tell(fp) ((fp)->fptr)
@ -324,46 +347,47 @@ TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the fil
#define f_rmdir(path) f_unlink(path)
#define f_unmount(path) f_mount(0, path, 0)
#ifndef EOF
#define EOF (-1)
#endif
/*--------------------------------------------------------------*/
/* Additional user defined functions */
/* Additional Functions */
/*--------------------------------------------------------------*/
/* RTC function */
/* RTC function (provided by user) */
#if !FF_FS_READONLY && !FF_FS_NORTC
DWORD get_fattime (void);
DWORD get_fattime (void); /* Get current time */
#endif
/* LFN support functions */
#if FF_USE_LFN >= 1 /* Code conversion (defined in unicode.c) */
/* LFN support functions (defined in ffunicode.c) */
#if FF_USE_LFN >= 1
WCHAR ff_oem2uni (WCHAR oem, WORD cp); /* OEM code to Unicode conversion */
WCHAR ff_uni2oem (DWORD uni, WORD cp); /* Unicode to OEM code conversion */
DWORD ff_wtoupper (DWORD uni); /* Unicode upper-case conversion */
#endif
#if FF_USE_LFN == 3 /* Dynamic memory allocation */
void* ff_memalloc (UINT msize); /* Allocate memory block */
void ff_memfree (void* mblock); /* Free memory block */
#endif
/* Sync functions */
#if FF_FS_REENTRANT
int ff_cre_syncobj (BYTE vol, FF_SYNC_t* sobj); /* Create a sync object */
int ff_req_grant (FF_SYNC_t sobj); /* Lock sync object */
void ff_rel_grant (FF_SYNC_t sobj); /* Unlock sync object */
int ff_del_syncobj (FF_SYNC_t sobj); /* Delete a sync object */
/* O/S dependent functions (samples available in ffsystem.c) */
#if FF_USE_LFN == 3 /* Dynamic memory allocation */
void* ff_memalloc (UINT msize); /* Allocate memory block */
void ff_memfree (void* mblock); /* Free memory block */
#endif
#if FF_FS_REENTRANT /* Sync functions */
int ff_mutex_create (int vol); /* Create a sync object */
void ff_mutex_delete (int vol); /* Delete a sync object */
int ff_mutex_take (int vol); /* Lock sync object */
void ff_mutex_give (int vol); /* Unlock sync object */
#endif
/*--------------------------------------------------------------*/
/* Flags and offset address */
/* Flags and Offset Address */
/*--------------------------------------------------------------*/
/* File access mode and open method flags (3rd argument of f_open) */
#define FA_READ 0x01

View File

@ -1,8 +1,8 @@
/*---------------------------------------------------------------------------/
/ FatFs Functional Configurations
/ Configurations of FatFs Module
/---------------------------------------------------------------------------*/
#define FFCONF_DEF 86604 /* Revision ID */
#define FFCONF_DEF 80286 /* Revision ID */
/*---------------------------------------------------------------------------/
/ Function Configurations
@ -15,7 +15,7 @@
/ and optional writing functions as well. */
#define FF_FS_MINIMIZE 1
#define FF_FS_MINIMIZE 0
/* This option defines minimization level to remove some basic API functions.
/
/ 0: Basic functions are fully enabled.
@ -25,14 +25,6 @@
/ 3: f_lseek() function is removed in addition to 2. */
#define FF_USE_STRFUNC 0
/* This option switches string functions, f_gets(), f_putc(), f_puts() and f_printf().
/
/ 0: Disable string functions.
/ 1: Enable without LF-CRLF conversion.
/ 2: Enable with LF-CRLF conversion. */
#define FF_USE_FIND 0
/* This option switches filtered directory read functions, f_findfirst() and
/ f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */
@ -64,11 +56,35 @@
/* This option switches f_forward() function. (0:Disable or 1:Enable) */
#define FF_USE_STRFUNC 0
#define FF_PRINT_LLI 1
#define FF_PRINT_FLOAT 1
#define FF_STRF_ENCODE 3
/* FF_USE_STRFUNC switches string functions, f_gets(), f_putc(), f_puts() and
/ f_printf().
/
/ 0: Disable. FF_PRINT_LLI, FF_PRINT_FLOAT and FF_STRF_ENCODE have no effect.
/ 1: Enable without LF-CRLF conversion.
/ 2: Enable with LF-CRLF conversion.
/
/ FF_PRINT_LLI = 1 makes f_printf() support long long argument and FF_PRINT_FLOAT = 1/2
/ makes f_printf() support floating point argument. These features want C99 or later.
/ When FF_LFN_UNICODE >= 1 with LFN enabled, string functions convert the character
/ encoding in it. FF_STRF_ENCODE selects assumption of character encoding ON THE FILE
/ to be read/written via those functions.
/
/ 0: ANSI/OEM in current CP
/ 1: Unicode in UTF-16LE
/ 2: Unicode in UTF-16BE
/ 3: Unicode in UTF-8
*/
/*---------------------------------------------------------------------------/
/ Locale and Namespace Configurations
/---------------------------------------------------------------------------*/
#define FF_CODE_PAGE 850
#define FF_CODE_PAGE 932
/* This option specifies the OEM code page to be used on the target system.
/ Incorrect code page setting can cause a file open failure.
/
@ -102,7 +118,7 @@
/* The FF_USE_LFN switches the support for LFN (long file name).
/
/ 0: Disable LFN. FF_MAX_LFN has no effect.
/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
/ 2: Enable LFN with dynamic working buffer on the STACK.
/ 3: Enable LFN with dynamic working buffer on the HEAP.
/
@ -110,14 +126,14 @@
/ requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and
/ additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled.
/ The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can
/ be in range of 12 to 255. It is recommended to be set 255 to fully support LFN
/ be in range of 12 to 255. It is recommended to be set it 255 to fully support LFN
/ specification.
/ When use stack for the working buffer, take care on stack overflow. When use heap
/ memory for the working buffer, memory management functions, ff_memalloc() and
/ ff_memfree() in ffsystem.c, need to be added to the project. */
/ ff_memfree() exemplified in ffsystem.c, need to be added to the project. */
#define FF_LFN_UNICODE 0
#define FF_LFN_UNICODE 2
/* This option switches the character encoding on the API when LFN is enabled.
/
/ 0: ANSI/OEM in current CP (TCHAR = char)
@ -137,19 +153,6 @@
/ on character encoding. When LFN is not enabled, these options have no effect. */
#define FF_STRF_ENCODE 0
/* When FF_LFN_UNICODE >= 1 with LFN enabled, string I/O functions, f_gets(),
/ f_putc(), f_puts and f_printf() convert the character encoding in it.
/ This option selects assumption of character encoding ON THE FILE to be
/ read/written via those functions.
/
/ 0: ANSI/OEM in current CP
/ 1: Unicode in UTF-16LE
/ 2: Unicode in UTF-16BE
/ 3: Unicode in UTF-8
*/
#define FF_FS_RPATH 0
/* This option configures support for relative path.
/
@ -175,7 +178,7 @@
/ logical drives. Number of items must not be less than FF_VOLUMES. Valid
/ characters for the volume ID strings are A-Z, a-z and 0-9, however, they are
/ compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is
/ not defined, a user defined volume string table needs to be defined as:
/ not defined, a user defined volume string table is needed as:
/
/ const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",...
*/
@ -187,37 +190,35 @@
/ number and only an FAT volume found on the physical drive will be mounted.
/ When this function is enabled (1), each logical drive number can be bound to
/ arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk()
/ funciton will be available. */
/ function will be available. */
#define FF_MIN_SS 512
#define FF_MAX_SS 512
/* This set of options configures the range of sector size to be supported. (512,
/ 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and
/ harddisk. But a larger value may be required for on-board flash memory and some
/ harddisk, but a larger value may be required for on-board flash memory and some
/ type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured
/ for variable sector size mode and disk_ioctl() function needs to implement
/ GET_SECTOR_SIZE command. */
#define FF_LBA64 1
/* This option switches support for 64-bit LBA. (0:Disable or 1:Enable)
/ To enable the 64-bit LBA, also exFAT needs to be enabled. (FF_FS_EXFAT == 1) */
#define FF_MIN_GPT 0x10000000
/* Minimum number of sectors to switch GPT as partitioning format in f_mkfs and
/ f_fdisk function. 0x100000000 max. This option has no effect when FF_LBA64 == 0. */
#define FF_USE_TRIM 0
/* This option switches support for ATA-TRIM. (0:Disable or 1:Enable)
/ To enable Trim function, also CTRL_TRIM command should be implemented to the
/ disk_ioctl() function. */
#define FF_FS_NOFSINFO 0
/* If you need to know correct free space on the FAT32 volume, set bit 0 of this
/ option, and f_getfree() function at first time after volume mount will force
/ a full FAT scan. Bit 1 controls the use of last allocated cluster number.
/
/ bit0=0: Use free cluster count in the FSINFO if available.
/ bit0=1: Do not trust free cluster count in the FSINFO.
/ bit1=0: Use last allocated cluster number in the FSINFO if available.
/ bit1=1: Do not trust last allocated cluster number in the FSINFO.
*/
/*---------------------------------------------------------------------------/
/ System Configurations
@ -230,24 +231,36 @@
/ buffer in the filesystem object (FATFS) is used for the file data transfer. */
#define FF_FS_EXFAT 0
#define FF_FS_EXFAT 1
/* This option switches support for exFAT filesystem. (0:Disable or 1:Enable)
/ To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1)
/ Note that enabling exFAT discards ANSI C (C89) compatibility. */
#define FF_FS_NORTC 0
#define FF_FS_NORTC 1
#define FF_NORTC_MON 1
#define FF_NORTC_MDAY 1
#define FF_NORTC_YEAR 2019
/* The option FF_FS_NORTC switches timestamp functiton. If the system does not have
/ any RTC function or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable
/ the timestamp function. Every object modified by FatFs will have a fixed timestamp
#define FF_NORTC_YEAR 2024
/* The option FF_FS_NORTC switches timestamp feature. If the system does not have
/ an RTC or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable the
/ timestamp feature. Every object modified by FatFs will have a fixed timestamp
/ defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time.
/ To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be
/ added to the project to read current time form real-time clock. FF_NORTC_MON,
/ FF_NORTC_MDAY and FF_NORTC_YEAR have no effect.
/ These options have no effect at read-only configuration (FF_FS_READONLY = 1). */
/ These options have no effect in read-only configuration (FF_FS_READONLY = 1). */
#define FF_FS_NOFSINFO 0
/* If you need to know correct free space on the FAT32 volume, set bit 0 of this
/ option, and f_getfree() function at the first time after volume mount will force
/ a full FAT scan. Bit 1 controls the use of last allocated cluster number.
/
/ bit0=0: Use free cluster count in the FSINFO if available.
/ bit0=1: Do not trust free cluster count in the FSINFO.
/ bit1=0: Use last allocated cluster number in the FSINFO if available.
/ bit1=1: Do not trust last allocated cluster number in the FSINFO.
*/
#define FF_FS_LOCK 0
@ -262,26 +275,21 @@
/ lock control is independent of re-entrancy. */
/* #include <somertos.h> // O/S definitions */
#define FF_FS_REENTRANT 0
#define FF_FS_TIMEOUT 1000
#define FF_SYNC_t HANDLE
/* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs
/ module itself. Note that regardless of this option, file access to different
/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
/ and f_fdisk() function, are always not re-entrant. Only file/directory access
/ to the same volume is under control of this function.
/ to the same volume is under control of this featuer.
/
/ 0: Disable re-entrancy. FF_FS_TIMEOUT and FF_SYNC_t have no effect.
/ 0: Disable re-entrancy. FF_FS_TIMEOUT have no effect.
/ 1: Enable re-entrancy. Also user provided synchronization handlers,
/ ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj()
/ function, must be added to the project. Samples are available in
/ option/syscall.c.
/ ff_mutex_create(), ff_mutex_delete(), ff_mutex_take() and ff_mutex_give()
/ function, must be added to the project. Samples are available in ffsystem.c.
/
/ The FF_FS_TIMEOUT defines timeout period in unit of time tick.
/ The FF_SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*,
/ SemaphoreHandle_t and etc. A header file for O/S definitions needs to be
/ included somewhere in the scope of ff.h. */
/ The FF_FS_TIMEOUT defines timeout period in unit of O/S time tick.
*/

129
include/core/fs_ext.h Normal file
View File

@ -0,0 +1,129 @@
/*
* fs_ext.h
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __FS_EXT_H__
#define __FS_EXT_H__
#ifdef __cplusplus
extern "C" {
#endif
#define GAMECARD_CERT_MAGIC 0x43455254 /* "CERT". */
/// Located at offset 0x7000 in the gamecard image.
typedef struct {
u8 signature[0x100]; ///< RSA-2048-PKCS#1 v1.5 with SHA-256 signature over the rest of the data.
u32 magic; ///< "CERT".
u32 version;
u8 kek_index;
u8 reserved[0x7];
u8 t1_card_device_id[0x10];
u8 iv[0x10];
u8 hw_key[0x10]; ///< Encrypted.
u8 data[0xC0]; ///< Encrypted.
} FsGameCardCertificate;
NXDT_ASSERT(FsGameCardCertificate, 0x200);
typedef enum {
FsCardId1MakerCode_MegaChips = 0xC2,
FsCardId1MakerCode_Lapis = 0xAE,
FsCardId1MakerCode_Unknown = 0x36 ///< Seen in TLoZ:TotK, SMBW and other modern releases.
} FsCardId1MakerCode;
typedef enum {
FsCardId1MemoryType_None = 0,
FsCardId1MemoryType_CardModeT1 = BIT(0),
FsCardId1MemoryType_CardModeT2 = BIT(1),
FsCardId1MemoryType_Unknown1 = BIT(2), ///< Related to CardMode?
FsCardId1MemoryType_IsNand = BIT(3), ///< 0: Rom, 1: Nand.
FsCardId1MemoryType_Unknown2 = BIT(4), ///< Related to Nand memory type?
FsCardId1MemoryType_IsLate = BIT(5), ///< 0: Fast, 1: Late.
FsCardId1MemoryType_Unknown3 = BIT(6),
FsCardId1MemoryType_Unknown4 = BIT(7),
FsCardId1MemoryType_Count = 8, ///< Total values supported by this enum.
///< Values defined in Atmosphère source code.
FsCardId1MemoryType_T1RomFast = FsCardId1MemoryType_CardModeT1,
FsCardId1MemoryType_T2RomFast = FsCardId1MemoryType_CardModeT2,
FsCardId1MemoryType_T1NandFast = (FsCardId1MemoryType_IsNand | FsCardId1MemoryType_CardModeT1),
FsCardId1MemoryType_T2NandFast = (FsCardId1MemoryType_IsNand | FsCardId1MemoryType_CardModeT2),
FsCardId1MemoryType_T1RomLate = (FsCardId1MemoryType_IsLate | FsCardId1MemoryType_CardModeT1),
FsCardId1MemoryType_T2RomLate = (FsCardId1MemoryType_IsLate | FsCardId1MemoryType_CardModeT2),
FsCardId1MemoryType_T1NandLate = (FsCardId1MemoryType_IsLate | FsCardId1MemoryType_IsNand | FsCardId1MemoryType_CardModeT1),
FsCardId1MemoryType_T2NandLate = (FsCardId1MemoryType_IsLate | FsCardId1MemoryType_IsNand | FsCardId1MemoryType_CardModeT2)
} FsCardId1MemoryType;
typedef struct {
u8 maker_code; ///< FsCardId1MakerCode.
u8 memory_capacity; ///< Matches GameCardRomSize.
u8 reserved; ///< Known values: 0x00, 0x01, 0x02, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0C, 0x0D, 0x0E, 0x80.
u8 memory_type; ///< FsCardId1MemoryType.
} FsCardId1;
NXDT_ASSERT(FsCardId1, 0x4);
typedef enum {
FsCardId2CardType_Rom = 0,
FsCardId2CardType_WritableDevT1 = 1,
FsCardId2CardType_WritableProdT1 = 2,
FsCardId2CardType_WritableDevT2 = 3,
FsCardId2CardType_WritableProdT2 = 4,
FsCardId2CardType_Count = 5 ///< Total values supported by this enum.
} FsCardId2CardType;
typedef struct {
u8 sel_t1_key; ///< Matches sel_t1_key value from GameCardHeader (usually 0x02).
u8 card_type; ///< FsCardId2CardType.
u8 reserved[0x2]; ///< Usually filled with zeroes.
} FsCardId2;
NXDT_ASSERT(FsCardId2, 0x4);
typedef struct {
u8 reserved[0x4]; ///< Usually filled with zeroes.
} FsCardId3;
NXDT_ASSERT(FsCardId3, 0x4);
/// Returned by fsDeviceOperatorGetGameCardIdSet.
typedef struct {
FsCardId1 id1; ///< Specifies maker code, memory capacity and memory type.
FsCardId2 id2; ///< Specifies card security number and card type.
FsCardId3 id3; ///< Always zero (so far).
} FsGameCardIdSet;
NXDT_ASSERT(FsGameCardIdSet, 0xC);
/// IFileSystemProxy.
Result fsOpenGameCardStorage(FsStorage *out, const FsGameCardHandle *handle, u32 partition);
Result fsOpenGameCardDetectionEventNotifier(FsEventNotifier *out);
/// IDeviceOperator.
Result fsDeviceOperatorUpdatePartitionInfo(FsDeviceOperator *d, const FsGameCardHandle *handle, u32 *out_title_version, u64 *out_title_id);
#ifdef __cplusplus
}
#endif
#endif /* __FS_EXT_H__ */

399
include/core/gamecard.h Normal file
View File

@ -0,0 +1,399 @@
/*
* gamecard.h
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __GAMECARD_H__
#define __GAMECARD_H__
#include "fs_ext.h"
#include "hfs.h"
#ifdef __cplusplus
extern "C" {
#endif
#define GAMECARD_HEAD_MAGIC 0x48454144 /* "HEAD". */
#define GAMECARD_PAGE_SIZE 0x200
#define GAMECARD_PAGE_OFFSET(x) ((u64)(x) * GAMECARD_PAGE_SIZE)
#define GAMECARD_UPDATE_TID SYSTEM_UPDATE_TID
#define GAMECARD_HEADER2_OFFSET 0x200
#define GAMECARD_HEADER2_CERT_OFFSET 0x400
#define GAMECARD_CERT_OFFSET 0x7000
/// Encrypted using AES-128-ECB with the common titlekek generator key (stored in the .rodata segment from the Lotus firmware).
typedef struct {
union {
u8 value[0x10];
struct {
u8 package_id[0x8]; ///< Matches package_id from GameCardHeader.
u8 reserved[0x8]; ///< Just zeroes.
};
};
} GameCardKeySource;
NXDT_ASSERT(GameCardKeySource, 0x10);
/// Plaintext area. Dumped from FS program memory.
typedef struct {
GameCardKeySource key_source;
u8 encrypted_titlekey[0x10]; ///< Encrypted using AES-128-CCM with the decrypted key_source and the nonce from this section.
u8 mac[0x10]; ///< Used to verify the validity of the decrypted titlekey.
u8 nonce[0xC]; ///< Used as the IV to decrypt encrypted_titlekey using AES-128-CCM.
u8 reserved[0x1C4];
} GameCardInitialData;
NXDT_ASSERT(GameCardInitialData, 0x200);
/// Encrypted using AES-128-CTR with the key and IV/counter from the `GameCardTitleKeyAreaEncryption` section. Assumed to be all zeroes in retail gamecards.
typedef struct {
u8 titlekey[0x10]; ///< Decrypted titlekey from the `GameCardInitialData` section.
u8 reserved[0xCF0];
} GameCardTitleKeyArea;
NXDT_ASSERT(GameCardTitleKeyArea, 0xD00);
/// Encrypted using RSA-2048-OAEP and a private OAEP key from AuthoringTool. Assumed to be all zeroes in retail gamecards.
typedef struct {
u8 titlekey_encryption_key[0x10]; ///< Used as the AES-128-CTR key for the `GameCardTitleKeyArea` section. Randomly generated during XCI creation by AuthoringTool.
u8 titlekey_encryption_iv[0x10]; ///< Used as the AES-128-CTR IV/counter for the `GameCardTitleKeyArea` section. Randomly generated during XCI creation by AuthoringTool.
u8 reserved[0xE0];
} GameCardTitleKeyAreaEncryption;
NXDT_ASSERT(GameCardTitleKeyAreaEncryption, 0x100);
/// Used to secure communications between the Lotus and the inserted gamecard.
/// Supposedly precedes the gamecard header.
typedef struct {
GameCardInitialData initial_data;
GameCardTitleKeyArea titlekey_area;
GameCardTitleKeyAreaEncryption titlekey_area_encryption;
} GameCardKeyArea;
NXDT_ASSERT(GameCardKeyArea, 0x1000);
/// Plaintext area. Dumped from FS program memory.
/// Overall structure may change with each new LAFW version.
typedef struct {
u32 asic_security_mode; ///< Determines how the Lotus ASIC initialised the gamecard security mode. Usually 0xFFFFFFF9.
u32 asic_status; ///< Bitmask of the internal gamecard interface status. Usually 0x20000000.
FsCardId1 card_id1;
FsCardId2 card_id2;
u8 card_uid[0x40];
u8 reserved[0x190];
u8 asic_session_hash[0x20]; ///< Changes with each gamecard (re)insertion.
} GameCardSpecificData;
NXDT_ASSERT(GameCardSpecificData, 0x200);
/// Plaintext area. Dumped from FS program memory.
/// This struct is returned by Lotus command "ChangeToSecureMode" (0xF). This means it is only available *after* the gamecard secure area has been mounted.
/// A copy of the gamecard header without the RSA-2048 signature and a plaintext GameCardInfo precedes this struct in FS program memory.
typedef struct {
GameCardSpecificData specific_data;
FsGameCardCertificate certificate;
u8 reserved[0x200];
GameCardInitialData initial_data;
} GameCardSecurityInformation;
NXDT_ASSERT(GameCardSecurityInformation, 0x800);
typedef enum {
GameCardKekIndex_Version0 = 0,
GameCardKekIndex_VersionForDev = 1,
GameCardKekIndex_Count = 2 ///< Total values supported by this enum.
} GameCardKekIndex;
typedef struct {
u8 kek_index : 4; ///< GameCardKekIndex.
u8 titlekey_dec_index : 4;
} GameCardKeyIndex;
NXDT_ASSERT(GameCardKeyIndex, 0x1);
typedef enum {
GameCardRomSize_1GiB = 0xFA,
GameCardRomSize_2GiB = 0xF8,
GameCardRomSize_4GiB = 0xF0,
GameCardRomSize_8GiB = 0xE0,
GameCardRomSize_16GiB = 0xE1,
GameCardRomSize_32GiB = 0xE2
} GameCardRomSize;
typedef enum {
GameCardFlags_None = 0,
GameCardFlags_AutoBoot = BIT(0),
GameCardFlags_HistoryErase = BIT(1),
GameCardFlags_RepairTool = BIT(2),
GameCardFlags_DifferentRegionCupToTerraDevice = BIT(3),
GameCardFlags_DifferentRegionCupToGlobalDevice = BIT(4),
GameCardFlags_HasCa10Certificate = BIT(7),
GameCardFlags_Count = 6 ///< Total values supported by this enum.
} GameCardFlags;
/// Available in HOS 18.0.0+.
typedef enum {
GameCardFlags2_None = 0,
GameCardFlags2_T1 = 1,
GameCardFlags2_T2 = 2,
GameCardFlags2_Count = 3 ///< Total values supported by this enum.
} GameCardFlags2;
typedef enum {
GameCardSelSec_ForT1 = 1,
GameCardSelSec_ForT2 = 2,
GameCardSelSec_Count = 2 ///< Total values supported by this enum.
} GameCardSelSec;
typedef enum {
GameCardFwVersion_ForDev = 0,
GameCardFwVersion_Since100NUP = 1, ///< upp_version >= 0 (0.0.0-0.0) in GameCardInfo.
GameCardFwVersion_Since400NUP = 2, ///< upp_version >= 268435456 (4.0.0-0.0) in GameCardInfo.
GameCardFwVersion_Since900NUP = 3, ///< upp_version >= 603979776 (9.0.0-0.0) in GameCardInfo. Seems to be unused.
GameCardFwVersion_Since1100NUP = 4, ///< upp_version >= 738197504 (11.0.0-0.0) in GameCardInfo.
GameCardFwVersion_Since1200NUP = 5, ///< upp_version >= 805306368 (12.0.0-0.0) in GameCardInfo.
GameCardFwVersion_Count = 6 ///< Total values supported by this enum.
} GameCardFwVersion;
typedef enum {
GameCardAccCtrl1_25MHz = 0xA10011,
GameCardAccCtrl1_50MHz = 0xA10010 ///< GameCardRomSize_8GiB or greater.
} GameCardAccCtrl1;
typedef enum {
GameCardCompatibilityType_Normal = 0,
GameCardCompatibilityType_Terra = 1,
GameCardCompatibilityType_Count = 2 ///< Total values supported by this enum.
} GameCardCompatibilityType;
/// Encrypted using AES-128-CBC with the XCI header key (found in FS program memory under HOS 9.0.0+) and the IV from `GameCardHeader`.
typedef struct {
u64 fw_version; ///< GameCardFwVersion.
u32 acc_ctrl_1; ///< GameCardAccCtrl1.
u32 wait_1_time_read; ///< Always 0x1388.
u32 wait_2_time_read; ///< Always 0.
u32 wait_1_time_write; ///< Always 0.
u32 wait_2_time_write; ///< Always 0.
Version fw_mode; ///< Current SDK version.
Version upp_version; ///< Bundled system update version.
u8 compatibility_type; ///< GameCardCompatibilityType.
u8 reserved_1[0x3];
u64 upp_hash; ///< Checksum for the update partition. The exact way it's calculated is currently unknown.
u64 upp_id; ///< Must match GAMECARD_UPDATE_TID.
u8 reserved_2[0x38];
} GameCardInfo;
NXDT_ASSERT(GameCardInfo, 0x70);
/// Placed after the `GameCardKeyArea` section.
typedef struct {
u8 signature[0x100]; ///< RSA-2048-PKCS#1 v1.5 with SHA-256 signature over the rest of the header.
u32 magic; ///< "HEAD".
u32 rom_area_start_page; ///< Expressed in GAMECARD_PAGE_SIZE units.
u32 backup_area_start_page; ///< Always 0xFFFFFFFF.
GameCardKeyIndex key_index;
u8 rom_size; ///< GameCardRomSize.
u8 version; ///< Always 0x00.
u8 flags; ///< GameCardFlags.
u8 package_id[0x8]; ///< Used for challenge-response authentication.
u32 valid_data_end_page; ///< Expressed in GAMECARD_PAGE_SIZE units.
u8 reserved_1;
u8 flags_2; ///< GameCardFlags2.
u8 reserved_2[0x2];
u8 card_info_iv[AES_128_KEY_SIZE]; ///< AES-128-CBC IV for the CardInfo area (reversed).
u64 partition_fs_header_address; ///< Root Hash File System header offset.
u64 partition_fs_header_size; ///< Root Hash File System header size.
u8 partition_fs_header_hash[SHA256_HASH_SIZE];
u8 initial_data_hash[SHA256_HASH_SIZE];
u32 sel_sec; ///< GameCardSelSec.
u32 sel_t1_key; ///< Always 0x02.
u32 sel_key; ///< Always 0x00.
u32 lim_area_page; ///< Expressed in GAMECARD_PAGE_SIZE units.
GameCardInfo card_info;
} GameCardHeader;
NXDT_ASSERT(GameCardHeader, 0x200);
/// Encrypted using AES-128-CBC.
typedef struct {
u8 unknown[0x40];
u8 header_hash[SHA256_HASH_SIZE];
u8 reserved[0x10];
} GameCardHeader2EncryptedData;
NXDT_ASSERT(GameCardHeader2EncryptedData, 0x70);
/// Placed immediately after the `GameCardHeader` section.
typedef struct {
u8 signature[0x100]; ///< RSA-2048-PKCS#1 v1.5 with SHA-256 signature over the rest of the header.
u8 unknown[0x90];
GameCardHeader2EncryptedData encrypted_data;
} GameCardHeader2;
NXDT_ASSERT(GameCardHeader2, 0x200);
/// Placed immediately after the `GameCardHeader2` section.
typedef struct {
u8 signature[0x100]; ///< RSA-2048-PKCS#1 v1.5 with SHA-256 signature over the data from 0x100 to 0x300.
u8 unknown_1[0x30];
u8 modulus[0x100]; ///< RSA modulus used to verify the signature from GameCardHeader2.
u8 exponent[0x4]; ///< RSA exponent used to verify the signature from GameCardHeader2.
u8 unknown_2[0x1CC];
} GameCardHeader2Certificate;
NXDT_ASSERT(GameCardHeader2Certificate, 0x400);
typedef enum {
GameCardStatus_NotInserted = 0, ///< No gamecard is inserted.
GameCardStatus_Processing = 1, ///< A gamecard has been inserted and it's being processed.
GameCardStatus_NoGameCardPatchEnabled = 2, ///< A gamecard has been inserted, but the running CFW enabled the "nogc" patch at boot.
///< This triggers an error whenever fsDeviceOperatorGetGameCardHandle is called. Nothing at all can be done with the inserted gamecard.
GameCardStatus_LotusAsicFirmwareUpdateRequired = 3, ///< A gamecard has been inserted, but a LAFW update is needed before being able to read the secure storage area.
///< Operations on the normal storage area are still possible, though.
GameCardStatus_InsertedAndInfoNotLoaded = 4, ///< A gamecard has been inserted, but an unexpected error unrelated to both "nogc" patch and LAFW version occurred.
GameCardStatus_InsertedAndInfoLoaded = 5, ///< A gamecard has been inserted and all required information could be successfully retrieved from it.
GameCardStatus_Count = 6 ///< Total values supported by this enum.
} GameCardStatus;
typedef enum {
LotusAsicFirmwareType_ReadFw = 0xFF,
LotusAsicFirmwareType_ReadDevFw = 0xFFFF,
LotusAsicFirmwareType_WriterFw = 0xFFFFFF,
LotusAsicFirmwareType_RmaFw = 0xFFFFFFFF
} LotusAsicFirmwareType;
typedef enum {
LotusAsicDeviceType_Test = 0,
LotusAsicDeviceType_Dev = 1,
LotusAsicDeviceType_Prod = 2,
LotusAsicDeviceType_Prod2Dev = 3,
LotusAsicDeviceType_Count = 4 ///< Total values supported by this enum.
} LotusAsicDeviceType;
/// Plaintext Lotus ASIC firmware (LAFW) blob. Dumped from FS program memory.
typedef struct {
u8 signature[0x100];
u32 magic; ///< "LAFW".
u32 fw_type; ///< LotusAsicFirmwareType.
u8 reserved_1[0x8];
struct {
u64 fw_version : 62; ///< Stored using a bitmask.
u64 device_type : 2; ///< LotusAsicDeviceType.
};
u32 data_size;
u8 reserved_2[0x4];
u8 data_iv[AES_128_KEY_SIZE];
char placeholder_str[0x10]; ///< "IDIDIDIDIDIDIDID".
u8 reserved_3[0x40];
u8 data[0x7680];
} LotusAsicFirmwareBlob;
NXDT_ASSERT(LotusAsicFirmwareBlob, 0x7800);
/// Initializes data needed to access raw gamecard storage areas.
/// Also spans a background thread to automatically detect gamecard status changes and to cache data from the inserted gamecard.
bool gamecardInitialize(void);
/// Deinitializes data generated by gamecardInitialize().
/// This includes destroying the background gamecard detection thread and freeing all cached gamecard data.
void gamecardExit(void);
/// Returns a user-mode gamecard status change event that can be used to wait for status changes on other threads.
/// If the gamecard interface hasn't been initialized, this returns NULL.
UEvent *gamecardGetStatusChangeUserEvent(void);
/// Returns the current GameCardStatus value.
u8 gamecardGetStatus(void);
/// Fills the provided GameCardSecurityInformation pointer.
/// This area can't be read using gamecardReadStorage().
bool gamecardGetSecurityInformation(GameCardSecurityInformation *out);
/// Fills the provided FsGameCardIdSet pointer.
/// This area can't be read using gamecardReadStorage().
bool gamecardGetCardIdSet(FsGameCardIdSet *out);
/// Fills the provided pointers with LAFW blob data from FS program memory.
/// 'out_lafw_blob' or 'out_lafw_version' may be set to NULL, but at least one of them must be a valid pointer.
bool gamecardGetLotusAsicFirmwareBlob(LotusAsicFirmwareBlob *out_lafw_blob, u64 *out_lafw_version);
/// Used to read raw data from the inserted gamecard. Supports unaligned reads.
/// All required handles, changes between normal <-> secure storage areas and proper offset calculations are managed internally.
/// 'offset' + 'read_size' must not exceed the value returned by gamecardGetTotalSize().
bool gamecardReadStorage(void *out, u64 read_size, u64 offset);
/// Fills the provided GameCardHeader pointer.
/// This area can also be read using gamecardReadStorage(), starting at offset 0.
bool gamecardGetHeader(GameCardHeader *out);
/// Fills the provided GameCardInfo pointer.
bool gamecardGetPlaintextCardInfoArea(GameCardInfo *out);
/// Fills the provided FsGameCardCertificate pointer.
/// This area can also be read using gamecardReadStorage(), starting at GAMECARD_CERT_OFFSET.
bool gamecardGetCertificate(FsGameCardCertificate *out);
/// Fills the provided u64 pointer with the total gamecard size, which is the size taken by both Normal and Secure storage areas.
bool gamecardGetTotalSize(u64 *out);
/// Fills the provided u64 pointer with the trimmed gamecard size, which is the same as the size returned by gamecardGetTotalSize() but using the trimmed Secure storage area size.
bool gamecardGetTrimmedSize(u64 *out);
/// Fills the provided u64 pointer with the gamecard ROM capacity, based on the GameCardRomSize value from the header. Not the same as gamecardGetTotalSize().
bool gamecardGetRomCapacity(u64 *out);
/// Fills the provided Version pointer with the bundled firmware update version in the inserted gamecard.
bool gamecardGetBundledFirmwareUpdateVersion(Version *out);
/// Fills the provided HashFileSystemContext pointer using information from the requested Hash FS partition.
/// Hash FS functions can be used on the retrieved HashFileSystemContext. hfsFreeContext() must be used to free the underlying data from the filled context.
bool gamecardGetHashFileSystemContext(u8 hfs_partition_type, HashFileSystemContext *out);
/// One-shot function to retrieve meaningful information from a Hash FS entry by name without using gamecardGetHashFileSystemContext() + Hash FS functions.
/// 'out_offset' or 'out_size' may be set to NULL, but at least one of them must be a valid pointer. The returned offset is always relative to the start of the gamecard image.
/// If you need to get entry information by index, just retrieve the Hash FS context for the target partition and use Hash FS functions on it.
bool gamecardGetHashFileSystemEntryInfoByName(u8 hfs_partition_type, const char *entry_name, u64 *out_offset, u64 *out_size);
/// Takes a GameCardFwVersion value. Returns a pointer to a string that represents the minimum HOS version that matches the provided LAFW version.
/// Returns NULL if the provided value is out of range.
const char *gamecardGetRequiredHosVersionString(u64 fw_version);
/// Takes a GameCardCompatibilityType value. Returns a pointer to a string that represents the provided compatibility type.
/// Returns NULL if the provided value is out of range.
const char *gamecardGetCompatibilityTypeString(u8 compatibility_type);
/// Takes a LotusAsicFirmwareType value. Returns a pointer to a string that represents the provided LAFW type.
/// Returns NULL if the provided value is invalid.
const char *gamecardGetLafwTypeString(u32 fw_type);
/// Takes a LotusAsicDeviceType value. Returns a pointer to a string that represents the provided LAFW device type.
/// Returns NULL if the provided value is out of range.
const char *gamecardGetLafwDeviceTypeString(u64 device_type);
#ifdef __cplusplus
}
#endif
#endif /* __GAMECARD_H__ */

148
include/core/hfs.h Normal file
View File

@ -0,0 +1,148 @@
/*
* hfs.h
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __HFS_H__
#define __HFS_H__
#ifdef __cplusplus
extern "C" {
#endif
#define HFS0_MAGIC 0x48465330 /* "HFS0". */
typedef struct {
u32 magic; ///< "HFS0".
u32 entry_count;
u32 name_table_size;
u8 reserved[0x4];
} HashFileSystemHeader;
NXDT_ASSERT(HashFileSystemHeader, 0x10);
typedef struct {
u64 offset;
u64 size;
u32 name_offset;
u32 hash_target_size;
u64 hash_target_offset;
u8 hash[SHA256_HASH_SIZE];
} HashFileSystemEntry;
NXDT_ASSERT(HashFileSystemEntry, 0x40);
typedef enum {
HashFileSystemPartitionType_None = 0, ///< Not a real value.
HashFileSystemPartitionType_Root = 1,
HashFileSystemPartitionType_Update = 2,
HashFileSystemPartitionType_Logo = 3, ///< Only available in GameCardFwVersion_Since400NUP or greater gamecards.
HashFileSystemPartitionType_Normal = 4,
HashFileSystemPartitionType_Secure = 5,
HashFileSystemPartitionType_Count = 6 ///< Total values supported by this enum.
} HashFileSystemPartitionType;
/// Internally used by gamecard functions.
/// Use gamecardGetHashFileSystemContext() to retrieve a Hash FS context.
typedef struct {
u8 type; ///< HashFileSystemPartitionType.
char *name; ///< Dynamically allocated partition name.
u64 offset; ///< Partition offset (relative to the start of gamecard image).
u64 size; ///< Partition size.
u64 header_size; ///< Full header size.
u8 *header; ///< HashFileSystemHeader + (HashFileSystemEntry * entry_count) + Name Table.
} HashFileSystemContext;
/// Reads raw partition data using a Hash FS context.
/// Input offset must be relative to the start of the Hash FS.
bool hfsReadPartitionData(HashFileSystemContext *ctx, void *out, u64 read_size, u64 offset);
/// Reads data from a previously retrieved HashFileSystemEntry using a Hash FS context.
/// Input offset must be relative to the start of the Hash FS entry.
bool hfsReadEntryData(HashFileSystemContext *ctx, HashFileSystemEntry *fs_entry, void *out, u64 read_size, u64 offset);
/// Calculates the extracted Hash FS size.
/// If the target partition is empty, 'out_size' will be set to zero and true will be returned.
bool hfsGetTotalDataSize(HashFileSystemContext *ctx, u64 *out_size);
/// Retrieves a Hash FS entry index by its name.
bool hfsGetEntryIndexByName(HashFileSystemContext *ctx, const char *name, u32 *out_idx);
/// Takes a HashFileSystemPartitionType value. Returns a pointer to a string that represents the partition name that matches the provided Hash FS partition type.
/// Returns NULL if the provided value is out of range.
const char *hfsGetPartitionNameString(u8 hfs_partition_type);
/// Miscellaneous functions.
NX_INLINE void hfsFreeContext(HashFileSystemContext *ctx)
{
if (!ctx) return;
if (ctx->name) free(ctx->name);
if (ctx->header) free(ctx->header);
memset(ctx, 0, sizeof(HashFileSystemContext));
}
NX_INLINE bool hfsIsValidContext(HashFileSystemContext *ctx)
{
return (ctx && ctx->type > HashFileSystemPartitionType_None && ctx->type < HashFileSystemPartitionType_Count && ctx->name && ctx->size && ctx->header_size && ctx->header);
}
NX_INLINE u32 hfsGetEntryCount(HashFileSystemContext *ctx)
{
return (hfsIsValidContext(ctx) ? ((HashFileSystemHeader*)ctx->header)->entry_count : 0);
}
NX_INLINE HashFileSystemEntry *hfsGetEntryByIndex(HashFileSystemContext *ctx, u32 idx)
{
return (idx < hfsGetEntryCount(ctx) ? (HashFileSystemEntry*)(ctx->header + sizeof(HashFileSystemHeader) + (idx * sizeof(HashFileSystemEntry))) : NULL);
}
NX_INLINE char *hfsGetNameTable(HashFileSystemContext *ctx)
{
u32 entry_count = hfsGetEntryCount(ctx);
return (entry_count ? (char*)(ctx->header + sizeof(HashFileSystemHeader) + (entry_count * sizeof(HashFileSystemEntry))) : NULL);
}
NX_INLINE char *hfsGetEntryName(HashFileSystemContext *ctx, HashFileSystemEntry *fs_entry)
{
char *name_table = hfsGetNameTable(ctx);
if (!name_table || !fs_entry || fs_entry->name_offset >= ((HashFileSystemHeader*)ctx->header)->name_table_size || !name_table[fs_entry->name_offset]) return NULL;
return (name_table + fs_entry->name_offset);
}
NX_INLINE char *hfsGetEntryNameByIndex(HashFileSystemContext *ctx, u32 idx)
{
HashFileSystemEntry *fs_entry = hfsGetEntryByIndex(ctx, idx);
char *name_table = hfsGetNameTable(ctx);
return ((fs_entry && name_table) ? (name_table + fs_entry->name_offset) : NULL);
}
NX_INLINE HashFileSystemEntry *hfsGetEntryByName(HashFileSystemContext *ctx, const char *name)
{
u32 idx = 0;
return (hfsGetEntryIndexByName(ctx, name, &idx) ? hfsGetEntryByIndex(ctx, idx) : NULL);
}
#ifdef __cplusplus
}
#endif
#endif /* __HFS_H__ */

View File

@ -0,0 +1,99 @@
/*
* hos_version_structs.h
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __HOS_VERSION_STRUCTS_H__
#define __HOS_VERSION_STRUCTS_H__
#ifdef __cplusplus
extern "C" {
#endif
/// Used to store version numbers expressed in dot notation: "{major}.{minor}.{micro}-{major_relstep}.{minor_relstep}".
/// Used by system version fields. 16-bit long relstep values were used by system version fields prior to HOS 3.0.0.
typedef struct {
union {
u32 value;
struct {
union {
u16 relstep;
struct {
u16 minor_relstep : 8;
u16 major_relstep : 8;
};
};
u16 micro : 4;
u16 minor : 6;
u16 major : 6;
};
};
} SystemVersion;
NXDT_ASSERT(SystemVersion, 0x4);
/// Used to store version numbers expressed in dot notation: "{release}.{private}".
/// Used by application version fields.
typedef struct {
union {
u32 value;
struct {
u32 private_ver : 16;
u32 release_ver : 16;
};
};
} ApplicationVersion;
NXDT_ASSERT(ApplicationVersion, 0x4);
/// Used to store version numbers expressed in dot notation: "{major}.{minor}.{micro}-{relstep}".
/// Used by SDK version fields.
typedef struct {
union {
u32 value;
struct {
u32 relstep : 8;
u32 micro : 8;
u32 minor : 8;
u32 major : 8;
};
};
} SdkAddOnVersion;
NXDT_ASSERT(SdkAddOnVersion, 0x4);
/// Convenient wrapper for all version structs.
typedef struct {
union {
u32 value;
SystemVersion system_version;
ApplicationVersion application_version;
SdkAddOnVersion sdk_addon_version;
};
} Version;
NXDT_ASSERT(Version, 0x4);
#ifdef __cplusplus
}
#endif
#endif /* __HOS_VERSION_STRUCTS_H__ */

77
include/core/http.h Normal file
View File

@ -0,0 +1,77 @@
/*
* http.h
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __HTTP_H__
#define __HTTP_H__
#include <curl/curl.h>
#ifdef __cplusplus
extern "C" {
#endif
/// Callback definition to write downloaded data.
typedef curl_write_callback HttpWriteCallback;
/// Callback definition to handle progress updates.
typedef curl_xferinfo_callback HttpProgressCallback;
/// Used by httpWriteBufferCallback().
typedef struct {
char *data; ///< Dynamically allocated buffer.
size_t size; ///< Buffer size.
} HttpBuffer;
/// Initializes the HTTP client interface.
bool httpInitialize(void);
/// Closes the HTTP client interface.
void httpExit(void);
/// Writes downloaded data to an output file. May be used as the write callback for httpPerformGetRequest().
/// Expects 'outstream' / 'write_ptr' to be a FILE pointer.
size_t httpWriteFileCallback(char *buffer, size_t size, size_t nitems, void *outstream);
/// Writes downloaded data to an output buffer. May be used as the write callback for httpPerformGetRequest().
/// Expects 'outstream' / 'write_ptr' to be a pointer to a HttpBuffer element. Its 'data' member is reallocated throughout the download process.
size_t httpWriteBufferCallback(char *buffer, size_t size, size_t nitems, void *outstream);
/// Performs a HTTP GET request. Blocks the calling thread until the whole transfer is complete.
/// Callbacks are optional, but they should be provided to save downloaded data and/or handle progress updates.
/// If 'outsize' is provided, the download size will be stored in it if the request succeeds.
bool httpPerformGetRequest(const char *url, bool force_https, size_t *outsize, HttpWriteCallback write_cb, void *write_ptr, HttpProgressCallback progress_cb, void *progress_ptr);
/// Wrapper for httpPerformGetRequest() + httpWriteFileCallback() that opens/closes the output file on its own.
/// Returns false if the request fails.
bool httpDownloadFile(const char *path, const char *url, bool force_https, HttpProgressCallback progress_cb, void *progress_ptr);
/// Wrapper for httpPerformGetRequest() + httpWriteBufferCallback() that manages a HttpBuffer element on its own.
/// Returns a pointer to a dynamically allocated buffer that holds the downloaded data, which must be freed by the user. This buffer is not NULL terminated.
/// Providing 'outsize' is mandatory. Returns NULL if the request fails.
char *httpDownloadData(size_t *outsize, const char *url, bool force_https, HttpProgressCallback progress_cb, void *progress_ptr);
#ifdef __cplusplus
}
#endif
#endif /* __HTTP_H__ */

176
include/core/key_sources.h Normal file
View File

@ -0,0 +1,176 @@
/*
* key_sources.h
*
* Copyright (c) 2019-2023, shchmue.
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/* Last updated on: 2024-03-31. */
/* Current key generation: NcaKeyGeneration_Since1800NUP (18 / 11). */
#pragma once
#ifndef __KEY_SOURCES_H__
#define __KEY_SOURCES_H__
#ifdef __cplusplus
extern "C" {
#endif
/* TODO: update on TSEC root key changes. */
#define TSEC_ROOT_KEY_VERSION 2
/* Used to derive all previous master keys using the latest master key on retail units. */
/* TODO: update on master key changes. */
static const u8 g_masterKeyVectorsProd[NcaKeyGeneration_Current][AES_128_KEY_SIZE] = {
{ 0x0C, 0xF0, 0x59, 0xAC, 0x85, 0xF6, 0x26, 0x65, 0xE1, 0xE9, 0x19, 0x55, 0xE6, 0xF2, 0x67, 0x3D }, ///< Zeroes encrypted with master key 00.
{ 0x29, 0x4C, 0x04, 0xC8, 0xEB, 0x10, 0xED, 0x9D, 0x51, 0x64, 0x97, 0xFB, 0xF3, 0x4D, 0x50, 0xDD }, ///< Master key 00 encrypted with master key 01.
{ 0xDE, 0xCF, 0xEB, 0xEB, 0x10, 0xAE, 0x74, 0xD8, 0xAD, 0x7C, 0xF4, 0x9E, 0x62, 0xE0, 0xE8, 0x72 }, ///< Master key 01 encrypted with master key 02.
{ 0x0A, 0x0D, 0xDF, 0x34, 0x22, 0x06, 0x6C, 0xA4, 0xE6, 0xB1, 0xEC, 0x71, 0x85, 0xCA, 0x4E, 0x07 }, ///< Master key 02 encrypted with master key 03.
{ 0x6E, 0x7D, 0x2D, 0xC3, 0x0F, 0x59, 0xC8, 0xFA, 0x87, 0xA8, 0x2E, 0xD5, 0x89, 0x5E, 0xF3, 0xE9 }, ///< Master key 03 encrypted with master key 04.
{ 0xEB, 0xF5, 0x6F, 0x83, 0x61, 0x9E, 0xF8, 0xFA, 0xE0, 0x87, 0xD7, 0xA1, 0x4E, 0x25, 0x36, 0xEE }, ///< Master key 04 encrypted with master key 05.
{ 0x1E, 0x1E, 0x22, 0xC0, 0x5A, 0x33, 0x3C, 0xB9, 0x0B, 0xA9, 0x03, 0x04, 0xBA, 0xDB, 0x07, 0x57 }, ///< Master key 05 encrypted with master key 06.
{ 0xA4, 0xD4, 0x52, 0x6F, 0xD1, 0xE4, 0x36, 0xAA, 0x9F, 0xCB, 0x61, 0x27, 0x1C, 0x67, 0x65, 0x1F }, ///< Master key 06 encrypted with master key 07.
{ 0xEA, 0x60, 0xB3, 0xEA, 0xCE, 0x8F, 0x24, 0x46, 0x7D, 0x33, 0x9C, 0xD1, 0xBC, 0x24, 0x98, 0x29 }, ///< Master key 07 encrypted with master key 08.
{ 0x4D, 0xD9, 0x98, 0x42, 0x45, 0x0D, 0xB1, 0x3C, 0x52, 0x0C, 0x9A, 0x44, 0xBB, 0xAD, 0xAF, 0x80 }, ///< Master key 08 encrypted with master key 09.
{ 0xB8, 0x96, 0x9E, 0x4A, 0x00, 0x0D, 0xD6, 0x28, 0xB3, 0xD1, 0xDB, 0x68, 0x5F, 0xFB, 0xE1, 0x2A }, ///< Master key 09 encrypted with master key 0A.
{ 0xC1, 0x8D, 0x16, 0xBB, 0x2A, 0xE4, 0x1D, 0xD4, 0xC2, 0xC1, 0xB6, 0x40, 0x94, 0x35, 0x63, 0x98 }, ///< Master key 0A encrypted with master key 0B.
{ 0xA3, 0x24, 0x65, 0x75, 0xEA, 0xCC, 0x6E, 0x8D, 0xFB, 0x5A, 0x16, 0x50, 0x74, 0xD2, 0x15, 0x06 }, ///< Master key 0B encrypted with master key 0C.
{ 0x83, 0x67, 0xAF, 0x01, 0xCF, 0x93, 0xA1, 0xAB, 0x80, 0x45, 0xF7, 0x3F, 0x72, 0xFD, 0x3B, 0x38 }, ///< Master key 0C encrypted with master key 0D.
{ 0xB1, 0x81, 0xA6, 0x0D, 0x72, 0xC7, 0xEE, 0x15, 0x21, 0xF3, 0xC0, 0xB5, 0x6B, 0x61, 0x6D, 0xE7 }, ///< Master key 0D encrypted with master key 0E.
{ 0xAF, 0x11, 0x4C, 0x67, 0x17, 0x7A, 0x52, 0x43, 0xF7, 0x70, 0x2F, 0xC7, 0xEF, 0x81, 0x72, 0x16 }, ///< Master key 0E encrypted with master key 0F.
{ 0x25, 0x12, 0x8B, 0xCB, 0xB5, 0x46, 0xA1, 0xF8, 0xE0, 0x52, 0x15, 0xB7, 0x0B, 0x57, 0x00, 0xBD }, ///< Master key 0F encrypted with master key 10.
{ 0x58, 0x15, 0xD2, 0xF6, 0x8A, 0xE8, 0x19, 0xAB, 0xFB, 0x2D, 0x52, 0x9D, 0xE7, 0x55, 0xF3, 0x93 }, ///< Master key 10 encrypted with master key 11.
};
/* Used to derive all previous master keys using the latest master key on development units. */
/* TODO: update on master key changes. */
static const u8 g_masterKeyVectorsDev[NcaKeyGeneration_Current][AES_128_KEY_SIZE] = {
{ 0x46, 0x22, 0xB4, 0x51, 0x9A, 0x7E, 0xA7, 0x7F, 0x62, 0xA1, 0x1F, 0x8F, 0xC5, 0x3A, 0xDB, 0xFE }, ///< Zeroes encrypted with master key 00.
{ 0x39, 0x33, 0xF9, 0x31, 0xBA, 0xE4, 0xA7, 0x21, 0x2C, 0xDD, 0xB7, 0xD8, 0xB4, 0x4E, 0x37, 0x23 }, ///< Master key 00 encrypted with master key 01.
{ 0x97, 0x29, 0xB0, 0x32, 0x43, 0x14, 0x8C, 0xA6, 0x85, 0xE9, 0x5A, 0x94, 0x99, 0x39, 0xAC, 0x5D }, ///< Master key 01 encrypted with master key 02.
{ 0x2C, 0xCA, 0x9C, 0x31, 0x1E, 0x07, 0xB0, 0x02, 0x97, 0x0A, 0xD8, 0x03, 0xA2, 0x76, 0x3F, 0xA3 }, ///< Master key 02 encrypted with master key 03.
{ 0x9B, 0x84, 0x76, 0x14, 0x72, 0x94, 0x52, 0xCB, 0x54, 0x92, 0x9B, 0xC4, 0x8C, 0x5B, 0x0F, 0xBA }, ///< Master key 03 encrypted with master key 04.
{ 0x78, 0xD5, 0xF1, 0x20, 0x3D, 0x16, 0xE9, 0x30, 0x32, 0x27, 0x34, 0x6F, 0xCF, 0xE0, 0x27, 0xDC }, ///< Master key 04 encrypted with master key 05.
{ 0x6F, 0xD2, 0x84, 0x1D, 0x05, 0xEC, 0x40, 0x94, 0x5F, 0x18, 0xB3, 0x81, 0x09, 0x98, 0x8D, 0x4E }, ///< Master key 05 encrypted with master key 06.
{ 0x37, 0xAF, 0xAB, 0x35, 0x79, 0x09, 0xD9, 0x48, 0x29, 0xD2, 0xDB, 0xA5, 0xA5, 0xF5, 0x30, 0x19 }, ///< Master key 06 encrypted with master key 07.
{ 0xEC, 0xE1, 0x46, 0x89, 0x37, 0xFD, 0xD2, 0x15, 0x8C, 0x3F, 0x24, 0x82, 0xEF, 0x49, 0x68, 0x04 }, ///< Master key 07 encrypted with master key 08.
{ 0x43, 0x3D, 0xC5, 0x3B, 0xEF, 0x91, 0x02, 0x21, 0x61, 0x54, 0x63, 0x8A, 0x35, 0xE7, 0xCA, 0xEE }, ///< Master key 08 encrypted with master key 09.
{ 0x6C, 0x2E, 0xCD, 0xB3, 0x34, 0x61, 0x77, 0xF5, 0xF9, 0xB1, 0xDD, 0x61, 0x98, 0x19, 0x3E, 0xD4 }, ///< Master key 09 encrypted with master key 0A.
{ 0x21, 0x88, 0x6B, 0x10, 0x9E, 0x83, 0xD6, 0x52, 0xAB, 0x08, 0xDB, 0x6D, 0x39, 0xFF, 0x1C, 0x9C }, ///< Master key 0A encrypted with master key 0B.
{ 0x8A, 0xCE, 0xC4, 0x7F, 0xBE, 0x08, 0x61, 0x88, 0xD3, 0x73, 0x64, 0x51, 0xE2, 0xB6, 0x53, 0x15 }, ///< Master key 0B encrypted with master key 0C.
{ 0x08, 0xE0, 0xF4, 0xBE, 0xAA, 0x6E, 0x5A, 0xC3, 0xA6, 0xBC, 0xFE, 0xB9, 0xE2, 0xA3, 0x24, 0x12 }, ///< Master key 0C encrypted with master key 0D.
{ 0xD6, 0x80, 0x98, 0xC0, 0xFA, 0xC7, 0x13, 0xCB, 0x93, 0xD2, 0x0B, 0x82, 0x4C, 0xA1, 0x7B, 0x8D }, ///< Master key 0D encrypted with master key 0E.
{ 0x78, 0x66, 0x19, 0xBD, 0x86, 0xE7, 0xC1, 0x09, 0x9B, 0x6F, 0x92, 0xB2, 0x58, 0x7D, 0xCF, 0x26 }, ///< Master key 0E encrypted with master key 0F.
{ 0x39, 0x1E, 0x7E, 0xF8, 0x7E, 0x73, 0xEA, 0x6F, 0xAF, 0x00, 0x3A, 0xB4, 0xAA, 0xB8, 0xB7, 0x59 }, ///< Master key 0F encrypted with master key 10.
{ 0x0C, 0x75, 0x39, 0x15, 0x53, 0xEA, 0x81, 0x11, 0xA3, 0xE0, 0xDC, 0x3D, 0x0E, 0x76, 0xC6, 0xB8 }, ///< Master key 10 encrypted with master key 11.
};
/* Used to derive a master KEK using the TSEC root key on Erista units. */
/* TODO: update on master key changes. */
static const u8 g_eristaMasterKekSource[AES_128_KEY_SIZE] = {
0x00, 0x04, 0x5D, 0xF0, 0x4D, 0xCD, 0x14, 0xA3, 0x1C, 0xBF, 0xDE, 0x48, 0x55, 0xBA, 0x35, 0xC1
};
/* Used to derive a master KEK on retail Mariko units. */
/* TODO: update on master key changes. */
static const u8 g_marikoMasterKekSourceProd[AES_128_KEY_SIZE] = {
0x4F, 0x41, 0x3C, 0x3B, 0xFB, 0x6A, 0x01, 0x2A, 0x68, 0x9F, 0x83, 0xE9, 0x53, 0xBD, 0x16, 0xD2
};
/* Used to derive a master KEK on development Mariko units. */
/* TODO: update on master key changes. */
static const u8 g_marikoMasterKekSourceDev[AES_128_KEY_SIZE] = {
0xE4, 0x45, 0xD0, 0x14, 0xA0, 0xE5, 0xE9, 0x4B, 0xFE, 0x76, 0xF4, 0x29, 0x41, 0xBB, 0x64, 0xED
};
/* Used to derive master keys from master KEKs. Found in TrustZone / Secure Monitor. */
static const u8 g_masterKeySource[AES_128_KEY_SIZE] = {
0xD8, 0xA2, 0x41, 0x0A, 0xC6, 0xC5, 0x90, 0x01, 0xC6, 0x1D, 0x6A, 0x26, 0x7C, 0x51, 0x3F, 0x3C
};
/* Randomly generated KEK source used to derive official CardInfo area keys. */
static const u8 g_gcCardInfoKekSource[AES_128_KEY_SIZE] = {
0xDE, 0xC6, 0x3F, 0x6A, 0xBF, 0x37, 0x72, 0x0B, 0x7E, 0x54, 0x67, 0x6A, 0x2D, 0xEF, 0xDD, 0x97
};
/* CardInfo area key used in retail units. Obfuscated using g_gcCardInfoKekSource and SMC AES engine keydata. */
/* Hardcoded because it can only be retrieved in plaintext form from FS program memory under HOS 9.0.0+ -- and we wish to use it under previous HOS versions as well. */
static const u8 g_gcCardInfoKeySourceProd[AES_128_KEY_SIZE] = {
0xF4, 0x92, 0x06, 0x52, 0xD6, 0x37, 0x70, 0xAF, 0xB1, 0x9C, 0x6F, 0x63, 0x09, 0x01, 0xF6, 0x29
};
/* CardInfo area key used in development units. Obfuscated using g_gcCardInfoKekSource and SMC AES engine keydata. */
/* Hardcoded because it can only be retrieved in plaintext form from FS program memory under HOS 9.0.0+ -- and we wish to use it under previous HOS versions as well. */
static const u8 g_gcCardInfoKeySourceDev[AES_128_KEY_SIZE] = {
0x54, 0xC3, 0xE1, 0xF2, 0x5B, 0x3A, 0x5E, 0xC0, 0x4C, 0xA7, 0xCF, 0xFB, 0xE1, 0xAE, 0x16, 0xCA
};
/* KEK source used to generate ticket common keys, which in turn are used to decrypt titlekeys from tickets. Also known as "titlekek_source". */
/* Found in TrustZone / Secure Monitor. */
static const u8 g_ticketCommonKeySource[AES_128_KEY_SIZE] = {
0x1E, 0xDC, 0x7B, 0x3B, 0x60, 0xE6, 0xB4, 0xD8, 0x78, 0xB8, 0x17, 0x15, 0x98, 0x5E, 0x62, 0x9B
};
/* Used by GenerateAesKek to derive keys. Found in TrustZone / Secure Monitor. */
static const u8 g_smcKeyTypeSources[SmcKeyType_Count][AES_128_KEY_SIZE] = {
[SmcKeyType_Default] = { 0x4D, 0x87, 0x09, 0x86, 0xC4, 0x5D, 0x20, 0x72, 0x2F, 0xBA, 0x10, 0x53, 0xDA, 0x92, 0xE8, 0xA9 }, ///< Also known as "aes_kek_generation_source".
[SmcKeyType_NormalOnly] = { 0x25, 0x03, 0x31, 0xFB, 0x25, 0x26, 0x0B, 0x79, 0x8C, 0x80, 0xD2, 0x69, 0x98, 0xE2, 0x22, 0x77 },
[SmcKeyType_RecoveryOnly] = { 0x76, 0x14, 0x1D, 0x34, 0x93, 0x2D, 0xE1, 0x84, 0x24, 0x7B, 0x66, 0x65, 0x55, 0x04, 0x65, 0x81 },
[SmcKeyType_NormalAndRecovery] = { 0xAF, 0x3D, 0xB7, 0xF3, 0x08, 0xA2, 0xD8, 0xA2, 0x08, 0xCA, 0x18, 0xA8, 0x69, 0x46, 0xC9, 0x0B },
};
/* Used by GenerateAesKek to derive keys. Found in TrustZone / Secure Monitor. */
static const u8 g_smcSealKeyMasks[SmcSealKey_Count][AES_128_KEY_SIZE] = {
[SmcSealKey_LoadAesKey] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
[SmcSealKey_DecryptDeviceUniqueData] = { 0xA2, 0xAB, 0xBF, 0x9C, 0x92, 0x2F, 0xBB, 0xE3, 0x78, 0x79, 0x9B, 0xC0, 0xCC, 0xEA, 0xA5, 0x74 },
[SmcSealKey_ImportLotusKey] = { 0x57, 0xE2, 0xD9, 0x45, 0xE4, 0x92, 0xF4, 0xFD, 0xC3, 0xF9, 0x86, 0x38, 0x89, 0x78, 0x9F, 0x3C },
[SmcSealKey_ImportEsDeviceKey] = { 0xE5, 0x4D, 0x9A, 0x02, 0xF0, 0x4F, 0x5F, 0xA8, 0xAD, 0x76, 0x0A, 0xF6, 0x32, 0x95, 0x59, 0xBB },
[SmcSealKey_ReencryptDeviceUniqueData] = { 0x59, 0xD9, 0x31, 0xF4, 0xA7, 0x97, 0xB8, 0x14, 0x40, 0xD6, 0xA2, 0x60, 0x2B, 0xED, 0x15, 0x31 },
[SmcSealKey_ImportSslKey] = { 0xFD, 0x6A, 0x25, 0xE5, 0xD8, 0x38, 0x7F, 0x91, 0x49, 0xDA, 0xF8, 0x59, 0xA8, 0x28, 0xE6, 0x75 },
[SmcSealKey_ImportEsClientCertKey] = { 0x89, 0x96, 0x43, 0x9A, 0x7C, 0xD5, 0x59, 0x55, 0x24, 0xD5, 0x24, 0x18, 0xAB, 0x6C, 0x04, 0x61 }
};
/* Used by GenerateAesKey. Found in SPL. */
static const u8 g_aesKeyGenerationSource[AES_128_KEY_SIZE] = {
0x89, 0x61, 0x5E, 0xE0, 0x5C, 0x31, 0xB6, 0x80, 0x5F, 0xE5, 0x8F, 0x3D, 0xA2, 0x4F, 0x7A, 0xA8
};
/* Used to derive the NCA header key. Found in the .rodata segment from the FS sysmodule. */
static const u8 g_ncaHeaderKekSource[AES_128_KEY_SIZE] = {
0x1F, 0x12, 0x91, 0x3A, 0x4A, 0xCB, 0xF0, 0x0D, 0x4C, 0xDE, 0x3A, 0xF6, 0xD5, 0x23, 0x88, 0x2A
};
/* Used to derive the NCA header key. Found in the .data segment from the FS sysmodule. */
static const u8 g_ncaHeaderKeySource[AES_128_KEY_SIZE * 2] = {
0x5A, 0x3E, 0xD8, 0x4F, 0xDE, 0xC0, 0xD8, 0x26, 0x31, 0xF7, 0xE2, 0x5D, 0x19, 0x7B, 0xF5, 0xD0,
0x1C, 0x9B, 0x7B, 0xFA, 0xF6, 0x28, 0x18, 0x3D, 0x71, 0xF6, 0x4D, 0x73, 0xF1, 0x50, 0xB9, 0xD2
};
/* Key sources used to derive NCA key area encryption keys required to handle key areas from NCA headers. Found in the .rodata segment from the FS sysmodule. */
static const u8 g_ncaKeyAreaEncryptionKeySources[NcaKeyAreaEncryptionKeyIndex_Count][AES_128_KEY_SIZE] = {
{ 0x7F, 0x59, 0x97, 0x1E, 0x62, 0x9F, 0x36, 0xA1, 0x30, 0x98, 0x06, 0x6F, 0x21, 0x44, 0xC3, 0x0D }, ///< Application.
{ 0x32, 0x7D, 0x36, 0x08, 0x5A, 0xD1, 0x75, 0x8D, 0xAB, 0x4E, 0x6F, 0xBA, 0xA5, 0x55, 0xD8, 0x82 }, ///< Ocean.
{ 0x87, 0x45, 0xF1, 0xBB, 0xA6, 0xBE, 0x79, 0x64, 0x7D, 0x04, 0x8B, 0xA6, 0x7B, 0x5F, 0xDA, 0x4A } ///< System.
};
#ifdef __cplusplus
}
#endif
#endif /* __KEY_SOURCES_H__ */

58
include/core/keys.h Normal file
View File

@ -0,0 +1,58 @@
/*
* keys.h
*
* Copyright (c) 2018-2020, SciresM.
* Copyright (c) 2019, shchmue.
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __KEYS_H__
#define __KEYS_H__
#ifdef __cplusplus
extern "C" {
#endif
/// Loads (and derives) keydata from sysmodule program memory, a keys file and hardcoded/obfuscated information.
/// Must be called (and succeed) before calling any of the functions below.
bool keysLoadKeyset(void);
/// Returns a pointer to the AES-128-XTS NCA header key, or NULL if keydata hasn't been loaded.
const u8 *keysGetNcaHeaderKey(void);
/// Returns a pointer to an AES-128-ECB NCA key area encryption key using the provided key area encryption key index and key generation values, or NULL if keydata hasn't been loaded.
const u8 *keysGetNcaKeyAreaEncryptionKey(u8 kaek_index, u8 key_generation);
/// Decrypts a RSA-OAEP wrapped titlekey using console-specific keydata.
/// 'rsa_wrapped_titlekey' must have a size of at least 0x100 bytes. 'out_titlekey' must have a size of at least AES_128_KEY_SIZE.
/// Returns false if an error occurs or if keydata hasn't been loaded.
bool keysDecryptRsaOaepWrappedTitleKey(const void *rsa_wrapped_titlekey, void *out_titlekey);
/// Returns a pointer to an AES-128-ECB ticket common key using the provided key generation value, or NULL if keydata hasn't been loaded.
const u8 *keysGetTicketCommonKey(u8 key_generation);
/// Returns a pointer to the AES-128-CBC CardInfo area key for gamecard headers, or NULL if keydata hasn't been loaded.
const u8 *keysGetGameCardInfoKey(void);
#ifdef __cplusplus
}
#endif
#endif /* __KEYS_H__ */

55
include/core/legal_info.h Normal file
View File

@ -0,0 +1,55 @@
/*
* legal_info.h
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __LEGAL_INFO_H__
#define __LEGAL_INFO_H__
#include "romfs.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
NcaContext *nca_ctx; ///< Pointer to the NCA context for the LegalInformation NCA from which XML data is retrieved.
char *authoring_tool_xml; ///< Pointer to a dynamically allocated, NULL-terminated buffer that holds AuthoringTool-like XML data.
u64 authoring_tool_xml_size; ///< Size for the AuthoringTool-like XML. This is essentially the same as using strlen() on 'authoring_tool_xml'.
} LegalInfoContext;
/// Initializes a LegalInfoContext using a previously initialized NcaContext (which must belong to a LegalInformation NCA).
bool legalInfoInitializeContext(LegalInfoContext *out, NcaContext *nca_ctx);
/// Helper inline functions.
NX_INLINE void legalInfoFreeContext(LegalInfoContext *legal_info_ctx)
{
if (!legal_info_ctx) return;
if (legal_info_ctx->authoring_tool_xml) free(legal_info_ctx->authoring_tool_xml);
memset(legal_info_ctx, 0, sizeof(LegalInfoContext));
}
#ifdef __cplusplus
}
#endif
#endif /* __LEGAL_INFO_H__ */

View File

@ -1,7 +1,7 @@
/*
* LZ4 - Fast LZ compression algorithm
* Header File
* Copyright (C) 2011-present, Yann Collet.
* Copyright (C) 2011-2020, Yann Collet.
BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
@ -46,7 +46,7 @@ extern "C" {
/**
Introduction
LZ4 is lossless compression algorithm, providing compression speed at 500 MB/s per core,
LZ4 is lossless compression algorithm, providing compression speed >500 MB/s per core,
scalable with multi-cores CPU. It features an extremely fast decoder, with speed in
multiple GB/s per core, typically reaching RAM speed limits on multi-core systems.
@ -58,16 +58,19 @@ extern "C" {
- unbounded multiple steps (described as Streaming compression)
lz4.h generates and decodes LZ4-compressed blocks (doc/lz4_Block_format.md).
Decompressing a block requires additional metadata, such as its compressed size.
Decompressing such a compressed block requires additional metadata.
Exact metadata depends on exact decompression function.
For the typical case of LZ4_decompress_safe(),
metadata includes block's compressed size, and maximum bound of decompressed size.
Each application is free to encode and pass such metadata in whichever way it wants.
lz4.h only handle blocks, it can not generate Frames.
Blocks are different from Frames (doc/lz4_Frame_format.md).
Frames bundle both blocks and metadata in a specified manner.
This are required for compressed data to be self-contained and portable.
Embedding metadata is required for compressed data to be self-contained and portable.
Frame format is delivered through a companion API, declared in lz4frame.h.
Note that the `lz4` CLI can only manage frames.
The `lz4` CLI can only manage frames.
*/
/*^***************************************************************
@ -94,64 +97,111 @@ extern "C" {
# define LZ4LIB_API LZ4LIB_VISIBILITY
#endif
/*! LZ4_FREESTANDING :
* When this macro is set to 1, it enables "freestanding mode" that is
* suitable for typical freestanding environment which doesn't support
* standard C library.
*
* - LZ4_FREESTANDING is a compile-time switch.
* - It requires the following macros to be defined:
* LZ4_memcpy, LZ4_memmove, LZ4_memset.
* - It only enables LZ4/HC functions which don't use heap.
* All LZ4F_* functions are not supported.
* - See tests/freestanding.c to check its basic setup.
*/
#if defined(LZ4_FREESTANDING) && (LZ4_FREESTANDING == 1)
# define LZ4_HEAPMODE 0
# define LZ4HC_HEAPMODE 0
# define LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION 1
# if !defined(LZ4_memcpy)
# error "LZ4_FREESTANDING requires macro 'LZ4_memcpy'."
# endif
# if !defined(LZ4_memset)
# error "LZ4_FREESTANDING requires macro 'LZ4_memset'."
# endif
# if !defined(LZ4_memmove)
# error "LZ4_FREESTANDING requires macro 'LZ4_memmove'."
# endif
#elif ! defined(LZ4_FREESTANDING)
# define LZ4_FREESTANDING 0
#endif
/*------ Version ------*/
#define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */
#define LZ4_VERSION_MINOR 9 /* for new (non-breaking) interface capabilities */
#define LZ4_VERSION_RELEASE 1 /* for tweaks, bug-fixes, or development */
#define LZ4_VERSION_RELEASE 4 /* for tweaks, bug-fixes, or development */
#define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE)
#define LZ4_LIB_VERSION LZ4_VERSION_MAJOR.LZ4_VERSION_MINOR.LZ4_VERSION_RELEASE
#define LZ4_QUOTE(str) #str
#define LZ4_EXPAND_AND_QUOTE(str) LZ4_QUOTE(str)
#define LZ4_VERSION_STRING LZ4_EXPAND_AND_QUOTE(LZ4_LIB_VERSION)
#define LZ4_VERSION_STRING LZ4_EXPAND_AND_QUOTE(LZ4_LIB_VERSION) /* requires v1.7.3+ */
LZ4LIB_API int LZ4_versionNumber (void); /**< library version number; useful to check dll version */
LZ4LIB_API const char* LZ4_versionString (void); /**< library version string; useful to check dll version */
LZ4LIB_API int LZ4_versionNumber (void); /**< library version number; useful to check dll version; requires v1.3.0+ */
LZ4LIB_API const char* LZ4_versionString (void); /**< library version string; useful to check dll version; requires v1.7.5+ */
/*-************************************
* Tuning parameter
**************************************/
#define LZ4_MEMORY_USAGE_MIN 10
#define LZ4_MEMORY_USAGE_DEFAULT 14
#define LZ4_MEMORY_USAGE_MAX 20
/*!
* LZ4_MEMORY_USAGE :
* Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.)
* Increasing memory usage improves compression ratio.
* Reduced memory usage may improve speed, thanks to better cache locality.
* Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; )
* Increasing memory usage improves compression ratio, at the cost of speed.
* Reduced memory usage may improve speed at the cost of ratio, thanks to better cache locality.
* Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache
*/
#ifndef LZ4_MEMORY_USAGE
# define LZ4_MEMORY_USAGE 14
# define LZ4_MEMORY_USAGE LZ4_MEMORY_USAGE_DEFAULT
#endif
#if (LZ4_MEMORY_USAGE < LZ4_MEMORY_USAGE_MIN)
# error "LZ4_MEMORY_USAGE is too small !"
#endif
#if (LZ4_MEMORY_USAGE > LZ4_MEMORY_USAGE_MAX)
# error "LZ4_MEMORY_USAGE is too large !"
#endif
/*-************************************
* Simple Functions
**************************************/
/*! LZ4_compress_default() :
Compresses 'srcSize' bytes from buffer 'src'
into already allocated 'dst' buffer of size 'dstCapacity'.
Compression is guaranteed to succeed if 'dstCapacity' >= LZ4_compressBound(srcSize).
It also runs faster, so it's a recommended setting.
If the function cannot compress 'src' into a more limited 'dst' budget,
compression stops *immediately*, and the function result is zero.
In which case, 'dst' content is undefined (invalid).
srcSize : max supported value is LZ4_MAX_INPUT_SIZE.
dstCapacity : size of buffer 'dst' (which must be already allocated)
@return : the number of bytes written into buffer 'dst' (necessarily <= dstCapacity)
or 0 if compression fails
Note : This function is protected against buffer overflow scenarios (never writes outside 'dst' buffer, nor read outside 'source' buffer).
*/
* Compresses 'srcSize' bytes from buffer 'src'
* into already allocated 'dst' buffer of size 'dstCapacity'.
* Compression is guaranteed to succeed if 'dstCapacity' >= LZ4_compressBound(srcSize).
* It also runs faster, so it's a recommended setting.
* If the function cannot compress 'src' into a more limited 'dst' budget,
* compression stops *immediately*, and the function result is zero.
* In which case, 'dst' content is undefined (invalid).
* srcSize : max supported value is LZ4_MAX_INPUT_SIZE.
* dstCapacity : size of buffer 'dst' (which must be already allocated)
* @return : the number of bytes written into buffer 'dst' (necessarily <= dstCapacity)
* or 0 if compression fails
* Note : This function is protected against buffer overflow scenarios (never writes outside 'dst' buffer, nor read outside 'source' buffer).
*/
LZ4LIB_API int LZ4_compress_default(const char* src, char* dst, int srcSize, int dstCapacity);
/*! LZ4_decompress_safe() :
compressedSize : is the exact complete size of the compressed block.
dstCapacity : is the size of destination buffer, which must be already allocated.
@return : the number of bytes decompressed into destination buffer (necessarily <= dstCapacity)
If destination buffer is not large enough, decoding will stop and output an error code (negative value).
If the source stream is detected malformed, the function will stop decoding and return a negative result.
Note : This function is protected against malicious data packets (never writes outside 'dst' buffer, nor read outside 'source' buffer).
*/
* compressedSize : is the exact complete size of the compressed block.
* dstCapacity : is the size of destination buffer (which must be already allocated), presumed an upper bound of decompressed size.
* @return : the number of bytes decompressed into destination buffer (necessarily <= dstCapacity)
* If destination buffer is not large enough, decoding will stop and output an error code (negative value).
* If the source stream is detected malformed, the function will stop decoding and return a negative result.
* Note 1 : This function is protected against malicious data packets :
* it will never writes outside 'dst' buffer, nor read outside 'source' buffer,
* even if the compressed block is maliciously modified to order the decoder to do these actions.
* In such case, the decoder stops immediately, and considers the compressed block malformed.
* Note 2 : compressedSize and dstCapacity must be provided to the function, the compressed block does not contain them.
* The implementation is free to send / store / derive this information in whichever way is most beneficial.
* If there is a need for a different format which bundles together both compressed data and its metadata, consider looking at lz4frame.h instead.
*/
LZ4LIB_API int LZ4_decompress_safe (const char* src, char* dst, int compressedSize, int dstCapacity);
@ -177,7 +227,8 @@ LZ4LIB_API int LZ4_compressBound(int inputSize);
The larger the acceleration value, the faster the algorithm, but also the lesser the compression.
It's a trade-off. It can be fine tuned, with each successive value providing roughly +~3% to speed.
An acceleration value of "1" is the same as regular LZ4_compress_default()
Values <= 0 will be replaced by ACCELERATION_DEFAULT (currently == 1, see lz4.c).
Values <= 0 will be replaced by LZ4_ACCELERATION_DEFAULT (currently == 1, see lz4.c).
Values > LZ4_ACCELERATION_MAX will be replaced by LZ4_ACCELERATION_MAX (currently == 65537, see lz4.c).
*/
LZ4LIB_API int LZ4_compress_fast (const char* src, char* dst, int srcSize, int dstCapacity, int acceleration);
@ -203,7 +254,18 @@ LZ4LIB_API int LZ4_compress_fast_extState (void* state, const char* src, char* d
* New value is necessarily <= input value.
* @return : Nb bytes written into 'dst' (necessarily <= targetDestSize)
* or 0 if compression fails.
*/
*
* Note : from v1.8.2 to v1.9.1, this function had a bug (fixed un v1.9.2+):
* the produced compressed content could, in specific circumstances,
* require to be decompressed into a destination buffer larger
* by at least 1 byte than the content to decompress.
* If an application uses `LZ4_compress_destSize()`,
* it's highly recommended to update liblz4 to v1.9.2 or better.
* If this can't be done or ensured,
* the receiving decompression function should provide
* a dstCapacity which is > decompressedSize, by at least 1 byte.
* See https://github.com/lz4/lz4/issues/859 for details
*/
LZ4LIB_API int LZ4_compress_destSize (const char* src, char* dst, int* srcSizePtr, int targetDstSize);
@ -211,25 +273,35 @@ LZ4LIB_API int LZ4_compress_destSize (const char* src, char* dst, int* srcSizePt
* Decompress an LZ4 compressed block, of size 'srcSize' at position 'src',
* into destination buffer 'dst' of size 'dstCapacity'.
* Up to 'targetOutputSize' bytes will be decoded.
* The function stops decoding on reaching this objective,
* which can boost performance when only the beginning of a block is required.
* The function stops decoding on reaching this objective.
* This can be useful to boost performance
* whenever only the beginning of a block is required.
*
* @return : the number of bytes decoded in `dst` (necessarily <= dstCapacity)
* @return : the number of bytes decoded in `dst` (necessarily <= targetOutputSize)
* If source stream is detected malformed, function returns a negative result.
*
* Note : @return can be < targetOutputSize, if compressed block contains less data.
* Note 1 : @return can be < targetOutputSize, if compressed block contains less data.
*
* Note 2 : this function features 2 parameters, targetOutputSize and dstCapacity,
* and expects targetOutputSize <= dstCapacity.
* It effectively stops decoding on reaching targetOutputSize,
* Note 2 : targetOutputSize must be <= dstCapacity
*
* Note 3 : this function effectively stops decoding on reaching targetOutputSize,
* so dstCapacity is kind of redundant.
* This is because in a previous version of this function,
* decoding operation would not "break" a sequence in the middle.
* As a consequence, there was no guarantee that decoding would stop at exactly targetOutputSize,
* This is because in older versions of this function,
* decoding operation would still write complete sequences.
* Therefore, there was no guarantee that it would stop writing at exactly targetOutputSize,
* it could write more bytes, though only up to dstCapacity.
* Some "margin" used to be required for this operation to work properly.
* This is no longer necessary.
* The function nonetheless keeps its signature, in an effort to not break API.
* Thankfully, this is no longer necessary.
* The function nonetheless keeps the same signature, in an effort to preserve API compatibility.
*
* Note 4 : If srcSize is the exact size of the block,
* then targetOutputSize can be any value,
* including larger than the block's decompressed size.
* The function will, at most, generate block's decompressed size.
*
* Note 5 : If srcSize is _larger_ than block's compressed size,
* then targetOutputSize **MUST** be <= block's decompressed size.
* Otherwise, *silent corruption will occur*.
*/
LZ4LIB_API int LZ4_decompress_safe_partial (const char* src, char* dst, int srcSize, int targetOutputSize, int dstCapacity);
@ -239,8 +311,25 @@ LZ4LIB_API int LZ4_decompress_safe_partial (const char* src, char* dst, int srcS
***********************************************/
typedef union LZ4_stream_u LZ4_stream_t; /* incomplete type (defined later) */
/**
Note about RC_INVOKED
- RC_INVOKED is predefined symbol of rc.exe (the resource compiler which is part of MSVC/Visual Studio).
https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros
- Since rc.exe is a legacy compiler, it truncates long symbol (> 30 chars)
and reports warning "RC4011: identifier truncated".
- To eliminate the warning, we surround long preprocessor symbol with
"#if !defined(RC_INVOKED) ... #endif" block that means
"skip this block when rc.exe is trying to read it".
*/
#if !defined(RC_INVOKED) /* https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros */
#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)
LZ4LIB_API LZ4_stream_t* LZ4_createStream(void);
LZ4LIB_API int LZ4_freeStream (LZ4_stream_t* streamPtr);
#endif /* !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) */
#endif
/*! LZ4_resetStream_fast() : v1.9.0+
* Use this to prepare an LZ4_stream_t for a new chain of dependent blocks
@ -324,8 +413,12 @@ typedef union LZ4_streamDecode_u LZ4_streamDecode_t; /* tracking context */
* creation / destruction of streaming decompression tracking context.
* A tracking context can be re-used multiple times.
*/
#if !defined(RC_INVOKED) /* https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros */
#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION)
LZ4LIB_API LZ4_streamDecode_t* LZ4_createStreamDecode(void);
LZ4LIB_API int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream);
#endif /* !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) */
#endif
/*! LZ4_setStreamDecode() :
* An LZ4_streamDecode_t context can be allocated once and re-used multiple times.
@ -375,7 +468,10 @@ LZ4LIB_API int LZ4_decoderRingBufferSize(int maxBlockSize);
* save the last 64KB of decoded data into a safe buffer where it can't be modified during decompression,
* then indicate where this data is saved using LZ4_setStreamDecode(), before decompressing next block.
*/
LZ4LIB_API int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* src, char* dst, int srcSize, int dstCapacity);
LZ4LIB_API int
LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode,
const char* src, char* dst,
int srcSize, int dstCapacity);
/*! LZ4_decompress_*_usingDict() :
@ -386,7 +482,18 @@ LZ4LIB_API int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecod
* Performance tip : Decompression speed can be substantially increased
* when dst == dictStart + dictSize.
*/
LZ4LIB_API int LZ4_decompress_safe_usingDict (const char* src, char* dst, int srcSize, int dstCapcity, const char* dictStart, int dictSize);
LZ4LIB_API int
LZ4_decompress_safe_usingDict(const char* src, char* dst,
int srcSize, int dstCapacity,
const char* dictStart, int dictSize);
LZ4LIB_API int
LZ4_decompress_safe_partial_usingDict(const char* src, char* dst,
int compressedSize,
int targetOutputSize, int maxOutputSize,
const char* dictStart, int dictSize);
#endif /* LZ4_H_2983827168210 */
/*^*************************************
@ -413,14 +520,17 @@ LZ4LIB_API int LZ4_decompress_safe_usingDict (const char* src, char* dst, int sr
* define LZ4_PUBLISH_STATIC_FUNCTIONS when building the LZ4 library.
******************************************************************************/
#ifdef LZ4_STATIC_LINKING_ONLY
#ifndef LZ4_STATIC_3504398509
#define LZ4_STATIC_3504398509
#ifdef LZ4_PUBLISH_STATIC_FUNCTIONS
#define LZ4LIB_STATIC_API LZ4LIB_API
#else
#define LZ4LIB_STATIC_API
#endif
#ifdef LZ4_STATIC_LINKING_ONLY
/*! LZ4_compress_fast_extState_fastReset() :
* A variant of LZ4_compress_fast_extState().
@ -460,80 +570,127 @@ LZ4LIB_STATIC_API int LZ4_compress_fast_extState_fastReset (void* state, const c
* stream (and source buffer) must remain in-place / accessible / unchanged
* through the completion of the first compression call on the stream.
*/
LZ4LIB_STATIC_API void LZ4_attach_dictionary(LZ4_stream_t* workingStream, const LZ4_stream_t* dictionaryStream);
LZ4LIB_STATIC_API void
LZ4_attach_dictionary(LZ4_stream_t* workingStream,
const LZ4_stream_t* dictionaryStream);
/*! In-place compression and decompression
*
* It's possible to have input and output sharing the same buffer,
* for highly constrained memory environments.
* In both cases, it requires input to lay at the end of the buffer,
* and decompression to start at beginning of the buffer.
* Buffer size must feature some margin, hence be larger than final size.
*
* |<------------------------buffer--------------------------------->|
* |<-----------compressed data--------->|
* |<-----------decompressed size------------------>|
* |<----margin---->|
*
* This technique is more useful for decompression,
* since decompressed size is typically larger,
* and margin is short.
*
* In-place decompression will work inside any buffer
* which size is >= LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize).
* This presumes that decompressedSize > compressedSize.
* Otherwise, it means compression actually expanded data,
* and it would be more efficient to store such data with a flag indicating it's not compressed.
* This can happen when data is not compressible (already compressed, or encrypted).
*
* For in-place compression, margin is larger, as it must be able to cope with both
* history preservation, requiring input data to remain unmodified up to LZ4_DISTANCE_MAX,
* and data expansion, which can happen when input is not compressible.
* As a consequence, buffer size requirements are much higher,
* and memory savings offered by in-place compression are more limited.
*
* There are ways to limit this cost for compression :
* - Reduce history size, by modifying LZ4_DISTANCE_MAX.
* Note that it is a compile-time constant, so all compressions will apply this limit.
* Lower values will reduce compression ratio, except when input_size < LZ4_DISTANCE_MAX,
* so it's a reasonable trick when inputs are known to be small.
* - Require the compressor to deliver a "maximum compressed size".
* This is the `dstCapacity` parameter in `LZ4_compress*()`.
* When this size is < LZ4_COMPRESSBOUND(inputSize), then compression can fail,
* in which case, the return code will be 0 (zero).
* The caller must be ready for these cases to happen,
* and typically design a backup scheme to send data uncompressed.
* The combination of both techniques can significantly reduce
* the amount of margin required for in-place compression.
*
* In-place compression can work in any buffer
* which size is >= (maxCompressedSize)
* with maxCompressedSize == LZ4_COMPRESSBOUND(srcSize) for guaranteed compression success.
* LZ4_COMPRESS_INPLACE_BUFFER_SIZE() depends on both maxCompressedSize and LZ4_DISTANCE_MAX,
* so it's possible to reduce memory requirements by playing with them.
*/
#define LZ4_DECOMPRESS_INPLACE_MARGIN(compressedSize) (((compressedSize) >> 8) + 32)
#define LZ4_DECOMPRESS_INPLACE_BUFFER_SIZE(decompressedSize) ((decompressedSize) + LZ4_DECOMPRESS_INPLACE_MARGIN(decompressedSize)) /**< note: presumes that compressedSize < decompressedSize. note2: margin is overestimated a bit, since it could use compressedSize instead */
#ifndef LZ4_DISTANCE_MAX /* history window size; can be user-defined at compile time */
# define LZ4_DISTANCE_MAX 65535 /* set to maximum value by default */
#endif
#define LZ4_COMPRESS_INPLACE_MARGIN (LZ4_DISTANCE_MAX + 32) /* LZ4_DISTANCE_MAX can be safely replaced by srcSize when it's smaller */
#define LZ4_COMPRESS_INPLACE_BUFFER_SIZE(maxCompressedSize) ((maxCompressedSize) + LZ4_COMPRESS_INPLACE_MARGIN) /**< maxCompressedSize is generally LZ4_COMPRESSBOUND(inputSize), but can be set to any lower value, with the risk that compression can fail (return code 0(zero)) */
#endif /* LZ4_STATIC_3504398509 */
#endif /* LZ4_STATIC_LINKING_ONLY */
#ifndef LZ4_H_98237428734687
#define LZ4_H_98237428734687
/*-************************************************************
* PRIVATE DEFINITIONS
* Private Definitions
**************************************************************
* Do not use these definitions directly.
* They are only exposed to allow static allocation of `LZ4_stream_t` and `LZ4_streamDecode_t`.
* Accessing members will expose code to API and/or ABI break in future versions of the library.
* Accessing members will expose user code to API and/or ABI break in future versions of the library.
**************************************************************/
#define LZ4_HASHLOG (LZ4_MEMORY_USAGE-2)
#define LZ4_HASHTABLESIZE (1 << LZ4_MEMORY_USAGE)
#define LZ4_HASH_SIZE_U32 (1 << LZ4_HASHLOG) /* required as macro for static allocation */
#if defined(__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
#include <stdint.h>
typedef struct LZ4_stream_t_internal LZ4_stream_t_internal;
struct LZ4_stream_t_internal {
uint32_t hashTable[LZ4_HASH_SIZE_U32];
uint32_t currentOffset;
uint16_t dirty;
uint16_t tableType;
const uint8_t* dictionary;
const LZ4_stream_t_internal* dictCtx;
uint32_t dictSize;
};
typedef struct {
const uint8_t* externalDict;
size_t extDictSize;
const uint8_t* prefixEnd;
size_t prefixSize;
} LZ4_streamDecode_t_internal;
# include <stdint.h>
typedef int8_t LZ4_i8;
typedef uint8_t LZ4_byte;
typedef uint16_t LZ4_u16;
typedef uint32_t LZ4_u32;
#else
typedef struct LZ4_stream_t_internal LZ4_stream_t_internal;
struct LZ4_stream_t_internal {
unsigned int hashTable[LZ4_HASH_SIZE_U32];
unsigned int currentOffset;
unsigned short dirty;
unsigned short tableType;
const unsigned char* dictionary;
const LZ4_stream_t_internal* dictCtx;
unsigned int dictSize;
};
typedef struct {
const unsigned char* externalDict;
const unsigned char* prefixEnd;
size_t extDictSize;
size_t prefixSize;
} LZ4_streamDecode_t_internal;
typedef signed char LZ4_i8;
typedef unsigned char LZ4_byte;
typedef unsigned short LZ4_u16;
typedef unsigned int LZ4_u32;
#endif
/*! LZ4_stream_t :
* information structure to track an LZ4 stream.
* LZ4_stream_t can also be created using LZ4_createStream(), which is recommended.
* The structure definition can be convenient for static allocation
* (on stack, or as part of larger structure).
* Init this structure with LZ4_initStream() before first use.
* note : only use this definition in association with static linking !
* this definition is not API/ABI safe, and may change in a future version.
*/
#define LZ4_STREAMSIZE_U64 ((1 << (LZ4_MEMORY_USAGE-3)) + 4 + ((sizeof(void*)==16) ? 4 : 0) /*AS-400*/ )
#define LZ4_STREAMSIZE (LZ4_STREAMSIZE_U64 * sizeof(unsigned long long))
* Never ever use below internal definitions directly !
* These definitions are not API/ABI safe, and may change in future versions.
* If you need static allocation, declare or allocate an LZ4_stream_t object.
**/
typedef struct LZ4_stream_t_internal LZ4_stream_t_internal;
struct LZ4_stream_t_internal {
LZ4_u32 hashTable[LZ4_HASH_SIZE_U32];
const LZ4_byte* dictionary;
const LZ4_stream_t_internal* dictCtx;
LZ4_u32 currentOffset;
LZ4_u32 tableType;
LZ4_u32 dictSize;
/* Implicit padding to ensure structure is aligned */
};
#define LZ4_STREAM_MINSIZE ((1UL << LZ4_MEMORY_USAGE) + 32) /* static size, for inter-version compatibility */
union LZ4_stream_u {
unsigned long long table[LZ4_STREAMSIZE_U64];
char minStateSize[LZ4_STREAM_MINSIZE];
LZ4_stream_t_internal internal_donotuse;
} ; /* previously typedef'd to LZ4_stream_t */
}; /* previously typedef'd to LZ4_stream_t */
/*! LZ4_initStream() : v1.9.0+
* An LZ4_stream_t structure must be initialized at least once.
@ -548,25 +705,30 @@ union LZ4_stream_u {
* In which case, the function will @return NULL.
* Note2: An LZ4_stream_t structure guarantees correct alignment and size.
* Note3: Before v1.9.0, use LZ4_resetStream() instead
*/
**/
LZ4LIB_API LZ4_stream_t* LZ4_initStream (void* buffer, size_t size);
/*! LZ4_streamDecode_t :
* information structure to track an LZ4 stream during decompression.
* init this structure using LZ4_setStreamDecode() before first use.
* note : only use in association with static linking !
* this definition is not API/ABI safe,
* and may change in a future version !
*/
#define LZ4_STREAMDECODESIZE_U64 (4 + ((sizeof(void*)==16) ? 2 : 0) /*AS-400*/ )
#define LZ4_STREAMDECODESIZE (LZ4_STREAMDECODESIZE_U64 * sizeof(unsigned long long))
* Never ever use below internal definitions directly !
* These definitions are not API/ABI safe, and may change in future versions.
* If you need static allocation, declare or allocate an LZ4_streamDecode_t object.
**/
typedef struct {
const LZ4_byte* externalDict;
const LZ4_byte* prefixEnd;
size_t extDictSize;
size_t prefixSize;
} LZ4_streamDecode_t_internal;
#define LZ4_STREAMDECODE_MINSIZE 32
union LZ4_streamDecode_u {
unsigned long long table[LZ4_STREAMDECODESIZE_U64];
char minStateSize[LZ4_STREAMDECODE_MINSIZE];
LZ4_streamDecode_t_internal internal_donotuse;
} ; /* previously typedef'd to LZ4_streamDecode_t */
/*-************************************
* Obsolete Functions
**************************************/
@ -585,34 +747,34 @@ union LZ4_streamDecode_u {
#ifdef LZ4_DISABLE_DEPRECATE_WARNINGS
# define LZ4_DEPRECATED(message) /* disable deprecation warnings */
#else
# define LZ4_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
# if defined (__cplusplus) && (__cplusplus >= 201402) /* C++14 or greater */
# define LZ4_DEPRECATED(message) [[deprecated(message)]]
# elif (LZ4_GCC_VERSION >= 405) || defined(__clang__)
# define LZ4_DEPRECATED(message) __attribute__((deprecated(message)))
# elif (LZ4_GCC_VERSION >= 301)
# define LZ4_DEPRECATED(message) __attribute__((deprecated))
# elif defined(_MSC_VER)
# define LZ4_DEPRECATED(message) __declspec(deprecated(message))
# elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ * 10 + __GNUC_MINOR__ >= 45))
# define LZ4_DEPRECATED(message) __attribute__((deprecated(message)))
# elif defined(__GNUC__) && (__GNUC__ * 10 + __GNUC_MINOR__ >= 31)
# define LZ4_DEPRECATED(message) __attribute__((deprecated))
# else
# pragma message("WARNING: You need to implement LZ4_DEPRECATED for this compiler")
# define LZ4_DEPRECATED(message)
# pragma message("WARNING: LZ4_DEPRECATED needs custom implementation for this compiler")
# define LZ4_DEPRECATED(message) /* disabled */
# endif
#endif /* LZ4_DISABLE_DEPRECATE_WARNINGS */
/* Obsolete compression functions */
LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress (const char* source, char* dest, int sourceSize);
LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress_limitedOutput (const char* source, char* dest, int sourceSize, int maxOutputSize);
/*! Obsolete compression functions (since v1.7.3) */
LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress (const char* src, char* dest, int srcSize);
LZ4_DEPRECATED("use LZ4_compress_default() instead") LZ4LIB_API int LZ4_compress_limitedOutput (const char* src, char* dest, int srcSize, int maxOutputSize);
LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_withState (void* state, const char* source, char* dest, int inputSize);
LZ4_DEPRECATED("use LZ4_compress_fast_extState() instead") LZ4LIB_API int LZ4_compress_limitedOutput_withState (void* state, const char* source, char* dest, int inputSize, int maxOutputSize);
LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize);
LZ4_DEPRECATED("use LZ4_compress_fast_continue() instead") LZ4LIB_API int LZ4_compress_limitedOutput_continue (LZ4_stream_t* LZ4_streamPtr, const char* source, char* dest, int inputSize, int maxOutputSize);
/* Obsolete decompression functions */
/*! Obsolete decompression functions (since v1.8.0) */
LZ4_DEPRECATED("use LZ4_decompress_fast() instead") LZ4LIB_API int LZ4_uncompress (const char* source, char* dest, int outputSize);
LZ4_DEPRECATED("use LZ4_decompress_safe() instead") LZ4LIB_API int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, int maxOutputSize);
/* Obsolete streaming functions; degraded functionality; do not use!
/* Obsolete streaming functions (since v1.7.0)
* degraded functionality; do not use!
*
* In order to perform streaming compression, these functions depended on data
* that is no longer tracked in the state. They have been preserved as well as
@ -626,23 +788,22 @@ LZ4_DEPRECATED("Use LZ4_createStream() instead") LZ4LIB_API int LZ4_sizeofStre
LZ4_DEPRECATED("Use LZ4_resetStream() instead") LZ4LIB_API int LZ4_resetStreamState(void* state, char* inputBuffer);
LZ4_DEPRECATED("Use LZ4_saveDict() instead") LZ4LIB_API char* LZ4_slideInputBuffer (void* state);
/* Obsolete streaming decoding functions */
/*! Obsolete streaming decoding functions (since v1.7.0) */
LZ4_DEPRECATED("use LZ4_decompress_safe_usingDict() instead") LZ4LIB_API int LZ4_decompress_safe_withPrefix64k (const char* src, char* dst, int compressedSize, int maxDstSize);
LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") LZ4LIB_API int LZ4_decompress_fast_withPrefix64k (const char* src, char* dst, int originalSize);
/*! LZ4_decompress_fast() : **unsafe!**
/*! Obsolete LZ4_decompress_fast variants (since v1.9.0) :
* These functions used to be faster than LZ4_decompress_safe(),
* but it has changed, and they are now slower than LZ4_decompress_safe().
* but this is no longer the case. They are now slower.
* This is because LZ4_decompress_fast() doesn't know the input size,
* and therefore must progress more cautiously in the input buffer to not read beyond the end of block.
* and therefore must progress more cautiously into the input buffer to not read beyond the end of block.
* On top of that `LZ4_decompress_fast()` is not protected vs malformed or malicious inputs, making it a security liability.
* As a consequence, LZ4_decompress_fast() is strongly discouraged, and deprecated.
*
* The last remaining LZ4_decompress_fast() specificity is that
* it can decompress a block without knowing its compressed size.
* Such functionality could be achieved in a more secure manner,
* by also providing the maximum size of input buffer,
* but it would require new prototypes, and adaptation of the implementation to this new use case.
* Such functionality can be achieved in a more secure manner
* by employing LZ4_decompress_safe_partial().
*
* Parameters:
* originalSize : is the uncompressed size to regenerate.
@ -657,7 +818,6 @@ LZ4_DEPRECATED("use LZ4_decompress_fast_usingDict() instead") LZ4LIB_API int LZ4
* But they may happen if input data is invalid (error or intentional tampering).
* As a consequence, use these functions in trusted environments with trusted data **only**.
*/
LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe() instead")
LZ4LIB_API int LZ4_decompress_fast (const char* src, char* dst, int originalSize);
LZ4_DEPRECATED("This function is deprecated and unsafe. Consider using LZ4_decompress_safe_continue() instead")
@ -674,7 +834,7 @@ LZ4LIB_API int LZ4_decompress_fast_usingDict (const char* src, char* dst, int or
LZ4LIB_API void LZ4_resetStream (LZ4_stream_t* streamPtr);
#endif /* LZ4_H_2983827168210 */
#endif /* LZ4_H_98237428734687 */
#if defined (__cplusplus)

70
include/core/mem.h Normal file
View File

@ -0,0 +1,70 @@
/*
* mem.h
*
* Copyright (c) 2019, shchmue.
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __MEM_H__
#define __MEM_H__
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
MemoryProgramSegmentType_None = 0,
MemoryProgramSegmentType_Text = BIT(0),
MemoryProgramSegmentType_Rodata = BIT(1),
MemoryProgramSegmentType_Data = BIT(2),
MemoryProgramSegmentType_All = (MemoryProgramSegmentType_Data | MemoryProgramSegmentType_Rodata | MemoryProgramSegmentType_Text),
MemoryProgramSegmentType_Limit = (MemoryProgramSegmentType_All + 1) ///< Placed here for convenience.
} MemoryProgramSegmentType;
typedef struct {
u64 program_id;
u8 mask; ///< MemoryProgramSegmentType. Used with memRetrieveProgramMemorySegment(). Ignored in memRetrieveFullProgramMemory().
u8 *data;
u64 data_size;
} MemoryLocation;
/// Retrieves memory segment (.text, .rodata, .data) data from a running program.
/// These are memory pages with read permission (Perm_R) enabled, with type MemType_CodeStatic or MemType_CodeMutable and no MemoryAttribute flag set.
bool memRetrieveProgramMemorySegment(MemoryLocation *location);
/// Retrieves full memory data from a running program.
/// These are any type of memory pages with read permission (Perm_R) enabled and no MemoryAttribute flag set.
/// MemType_Unmapped, MemType_Io, MemType_ThreadLocal and MemType_Reserved memory pages are excluded if FS program memory is being retrieved, in order to avoid hangs.
bool memRetrieveFullProgramMemory(MemoryLocation *location);
/// Frees a populated MemoryLocation element.
NX_INLINE void memFreeMemoryLocation(MemoryLocation *location)
{
if (!location) return;
if (location->data) free(location->data);
location->data = NULL;
location->data_size = 0;
}
#ifdef __cplusplus
}
#endif
#endif /* __MEM_H__ */

547
include/core/nacp.h Normal file
View File

@ -0,0 +1,547 @@
/*
* nacp.h
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __NACP_H__
#define __NACP_H__
#include "romfs.h"
#ifdef __cplusplus
extern "C" {
#endif
#define NACP_MAX_ICON_SIZE 0x20000 /* 128 KiB. */
typedef struct {
char name[0x200];
char publisher[0x100];
} NacpTitle;
NXDT_ASSERT(NacpTitle, 0x300);
typedef enum {
NacpStartupUserAccount_None = 0,
NacpStartupUserAccount_Required = 1,
NacpStartupUserAccount_RequiredWithNetworkServiceAccountAvailable = 2,
NacpStartupUserAccount_Count = 3 ///< Total values supported by this enum.
} NacpStartupUserAccount;
typedef enum {
NacpUserAccountSwitchLock_Disable = 0,
NacpUserAccountSwitchLock_Enable = 1,
NacpUserAccountSwitchLock_Count = 2, ///< Total values supported by this enum.
// Old.
NacpTouchScreenUsage_None = 0,
NacpTouchScreenUsage_Supported = 1,
NacpTouchScreenUsage_Required = 2
} NacpUserAccountSwitchLock;
typedef enum {
NacpAddOnContentRegistrationType_AllOnLaunch = 0,
NacpAddOnContentRegistrationType_OnDemand = 1,
NacpAddOnContentRegistrationType_Count = 2 ///< Total values supported by this enum.
} NacpAddOnContentRegistrationType;
typedef enum {
NacpAttribute_None = 0,
NacpAttribute_Demo = BIT(0),
NacpAttribute_RetailInteractiveDisplay = BIT(1),
NacpAttribute_DownloadPlay = BIT(2), ///< Removed.
NacpAttribute_Count = 3 ///< Total values supported by this enum.
} NacpAttribute;
/// Indexes used to access NACP Title structs.
typedef enum {
NacpLanguage_AmericanEnglish = 0,
NacpLanguage_BritishEnglish = 1,
NacpLanguage_Japanese = 2,
NacpLanguage_French = 3,
NacpLanguage_German = 4,
NacpLanguage_LatinAmericanSpanish = 5,
NacpLanguage_Spanish = 6,
NacpLanguage_Italian = 7,
NacpLanguage_Dutch = 8,
NacpLanguage_CanadianFrench = 9,
NacpLanguage_Portuguese = 10,
NacpLanguage_Russian = 11,
NacpLanguage_Korean = 12,
NacpLanguage_TraditionalChinese = 13,
NacpLanguage_SimplifiedChinese = 14,
NacpLanguage_BrazilianPortuguese = 15,
NacpLanguage_Count = 16, ///< Total values supported by this enum.
/// Old.
NacpLanguage_Taiwanese = NacpLanguage_TraditionalChinese,
NacpLanguage_Chinese = NacpLanguage_SimplifiedChinese
} NacpLanguage;
typedef enum {
NacpSupportedLanguage_None = 0,
NacpSupportedLanguage_AmericanEnglish = BIT(NacpLanguage_AmericanEnglish),
NacpSupportedLanguage_BritishEnglish = BIT(NacpLanguage_BritishEnglish),
NacpSupportedLanguage_Japanese = BIT(NacpLanguage_Japanese),
NacpSupportedLanguage_French = BIT(NacpLanguage_French),
NacpSupportedLanguage_German = BIT(NacpLanguage_German),
NacpSupportedLanguage_LatinAmericanSpanish = BIT(NacpLanguage_LatinAmericanSpanish),
NacpSupportedLanguage_Spanish = BIT(NacpLanguage_Spanish),
NacpSupportedLanguage_Italian = BIT(NacpLanguage_Italian),
NacpSupportedLanguage_Dutch = BIT(NacpLanguage_Dutch),
NacpSupportedLanguage_CanadianFrench = BIT(NacpLanguage_CanadianFrench),
NacpSupportedLanguage_Portuguese = BIT(NacpLanguage_Portuguese),
NacpSupportedLanguage_Russian = BIT(NacpLanguage_Russian),
NacpSupportedLanguage_Korean = BIT(NacpLanguage_Korean),
NacpSupportedLanguage_TraditionalChinese = BIT(NacpLanguage_TraditionalChinese),
NacpSupportedLanguage_SimplifiedChinese = BIT(NacpLanguage_SimplifiedChinese),
NacpSupportedLanguage_BrazilianPortuguese = BIT(NacpLanguage_BrazilianPortuguese),
NacpSupportedLanguage_Count = NacpLanguage_Count, ///< Total values supported by this enum.
///< Old.
NacpSupportedLanguage_Taiwanese = NacpSupportedLanguage_TraditionalChinese,
NacpSupportedLanguage_Chinese = NacpSupportedLanguage_SimplifiedChinese
} NacpSupportedLanguage;
typedef enum {
NacpParentalControl_None = 0,
NacpParentalControl_FreeCommunication = BIT(0),
NacpParentalControl_Count = 1 ///< Total values supported by this enum.
} NacpParentalControl;
typedef enum {
NacpScreenshot_Allow = 0,
NacpScreenshot_Deny = 1,
NacpScreenshot_Count = 2 ///< Total values supported by this enum.
} NacpScreenshot;
typedef enum {
NacpVideoCapture_Disable = 0,
NacpVideoCapture_Manual = 1,
NacpVideoCapture_Enable = 2,
NacpVideoCapture_Count = 3, ///< Total values supported by this enum.
/// Old.
NacpVideoCapture_Deny = NacpVideoCapture_Disable,
NacpVideoCapture_Allow = NacpVideoCapture_Manual
} NacpVideoCapture;
typedef enum {
NacpDataLossConfirmation_None = 0,
NacpDataLossConfirmation_Required = 1,
NacpDataLossConfirmation_Count = 2 ///< Total values supported by this enum.
} NacpDataLossConfirmation;
typedef enum {
NacpPlayLogPolicy_Open = 0,
NacpPlayLogPolicy_LogOnly = 1,
NacpPlayLogPolicy_None = 2,
NacpPlayLogPolicy_Closed = 3,
NacpPlayLogPolicy_Count = 4, ///< Total values supported by this enum.
/// Old.
NacpPlayLogPolicy_All = NacpPlayLogPolicy_Open
} NacpPlayLogPolicy;
/// Indexes used to access NACP RatingAge info.
typedef enum {
NacpRatingAgeOrganization_CERO = 0,
NacpRatingAgeOrganization_GRACGCRB = 1,
NacpRatingAgeOrganization_GSRMR = 2,
NacpRatingAgeOrganization_ESRB = 3,
NacpRatingAgeOrganization_ClassInd = 4,
NacpRatingAgeOrganization_USK = 5,
NacpRatingAgeOrganization_PEGI = 6,
NacpRatingAgeOrganization_PEGIPortugal = 7,
NacpRatingAgeOrganization_PEGIBBFC = 8,
NacpRatingAgeOrganization_Russian = 9,
NacpRatingAgeOrganization_ACB = 10,
NacpRatingAgeOrganization_OFLC = 11,
NacpRatingAgeOrganization_IARCGeneric = 12,
NacpRatingAgeOrganization_Count = 13 ///< Total values supported by this enum.
} NacpRatingAgeOrganization;
typedef struct {
s8 cero;
s8 grac_gcrb;
s8 gsrmr;
s8 esrb;
s8 class_ind;
s8 usk;
s8 pegi;
s8 pegi_portugal;
s8 pegi_bbfc;
s8 russian;
s8 acb;
s8 oflc;
s8 iarc_generic;
s8 reserved[0x13];
} NacpRatingAge;
NXDT_ASSERT(NacpRatingAge, 0x20);
typedef enum {
NacpLogoType_LicensedByNintendo = 0,
NacpLogoType_DistributedByNintendo = 1, ///< Removed.
NacpLogoType_Nintendo = 2,
NacpLogoType_Count = 3 ///< Total values supported by this enum.
} NacpLogoType;
typedef enum {
NacpLogoHandling_Auto = 0,
NacpLogoHandling_Manual = 1,
NacpLogoHandling_Count = 2 ///< Total values supported by this enum.
} NacpLogoHandling;
typedef enum {
NacpRuntimeAddOnContentInstall_Deny = 0,
NacpRuntimeAddOnContentInstall_AllowAppend = 1,
NacpRuntimeAddOnContentInstall_AllowAppendButDontDownloadWhenUsingNetwork = 2,
NacpRuntimeAddOnContentInstall_Count = 3 ///< Total values supported by this enum.
} NacpRuntimeAddOnContentInstall;
typedef enum {
NacpRuntimeParameterDelivery_Always = 0,
NacpRuntimeParameterDelivery_AlwaysIfUserStateMatched = 1,
NacpRuntimeParameterDelivery_OnRestart = 2,
NacpRuntimeParameterDelivery_Count = 3 ///< Total values supported by this enum.
} NacpRuntimeParameterDelivery;
typedef enum {
NacpAppropriateAgeForChina_None = 0,
NacpAppropriateAgeForChina_Age8 = 1,
NacpAppropriateAgeForChina_Age12 = 2,
NacpAppropriateAgeForChina_Age16 = 3,
NacpAppropriateAgeForChina_Count = 4 ///< Total values supported by this enum.
} NacpAppropriateAgeForChina;
typedef enum {
NacpUndecidedParameter75b8b_A = 0,
NacpUndecidedParameter75b8b_B = 1,
NacpUndecidedParameter75b8b_Count = 2 ///< Total values supported by this enum.
} NacpUndecidedParameter75b8b;
typedef enum {
NacpCrashReport_Deny = 0,
NacpCrashReport_Allow = 1,
NacpCrashReport_Count = 2 ///< Total values supported by this enum.
} NacpCrashReport;
typedef enum {
NacpHdcp_None = 0,
NacpHdcp_Required = 1,
NacpHdcp_Count = 2 ///< Total values supported by this enum.
} NacpHdcp;
typedef enum {
NacpStartupUserAccountOption_None = 0,
NacpStartupUserAccountOption_IsOptional = BIT(0),
NacpStartupUserAccountOption_Count = 1 ///< Total values supported by this enum.
} NacpStartupUserAccountOption;
typedef enum {
NacpRuntimeUpgrade_Deny = 0,
NacpRuntimeUpgrade_Allow = 1,
NacpRuntimeUpgrade_Count = 2 ///< Total values supported by this enum.
} NacpRuntimeUpgrade;
typedef enum {
NacpSupportingLimitedApplicationLicenses_None = 0,
NacpSupportingLimitedApplicationLicenses_Demo = BIT(0),
NacpSupportingLimitedApplicationLicenses_Count = 1 ///< Total values supported by this enum.
} NacpSupportingLimitedApplicationLicenses;
typedef enum {
NacpPlayLogQueryCapability_None = 0,
NacpPlayLogQueryCapability_WhiteList = 1,
NacpPlayLogQueryCapability_All = 2,
NacpPlayLogQueryCapability_Count = 3 ///< Total values supported by this enum.
} NacpPlayLogQueryCapability;
typedef enum {
NacpRepair_None = 0,
NacpRepair_SuppressGameCardAccess = BIT(0),
NacpRepair_Count = 1 ///< Total values supported by this enum.
} NacpRepair;
typedef enum {
NacpRequiredNetworkServiceLicenseOnLaunch_None = 0,
NacpRequiredNetworkServiceLicenseOnLaunch_Common = BIT(0),
NacpRequiredNetworkServiceLicenseOnLaunch_Count = 1 ///< Total values supported by this enum.
} NacpRequiredNetworkServiceLicenseOnLaunch;
typedef enum {
NacpJitConfigurationFlag_None = 0,
NacpJitConfigurationFlag_Enabled = BITL(0),
NacpJitConfigurationFlag_Count = 1 ///< Total values supported by this enum.
} NacpJitConfigurationFlag;
typedef struct {
u64 jit_configuration_flag; ///< NacpJitConfigurationFlag.
u64 memory_size;
} NacpJitConfiguration;
NXDT_ASSERT(NacpJitConfiguration, 0x10);
typedef enum {
NacpRequiredAddOnContentsSetDescriptorFlag_None = 0,
NacpRequiredAddOnContentsSetDescriptorFlag_Continue = 1,
NacpRequiredAddOnContentsSetDescriptorFlag_Count = 2 ///< Total values supported by this enum.
} NacpRequiredAddOnContentsSetDescriptorFlag;
typedef struct {
u16 index : 15;
u16 flag : 1; ///< NacpRequiredAddOnContentsSetDescriptorFlag.
} NacpRequiredAddOnContentsSetDescriptor;
NXDT_ASSERT(NacpRequiredAddOnContentsSetDescriptor, 0x2);
typedef struct {
NacpRequiredAddOnContentsSetDescriptor descriptors[0x20];
} NacpRequiredAddOnContentsSetBinaryDescriptor;
NXDT_ASSERT(NacpRequiredAddOnContentsSetBinaryDescriptor, 0x40);
typedef enum {
NacpPlayReportPermission_None = 0,
NacpPlayReportPermission_TargetMarketing = BIT(0),
NacpPlayReportPermission_Count = 1 ///< Total values supported by this enum.
} NacpPlayReportPermission;
typedef enum {
NacpCrashScreenshotForProd_Deny = 0,
NacpCrashScreenshotForProd_Allow = 1,
NacpCrashScreenshotForProd_Count = 2 ///< Total values supported by this enum.
} NacpCrashScreenshotForProd;
typedef enum {
NacpCrashScreenshotForDev_Deny = 0,
NacpCrashScreenshotForDev_Allow = 1,
NacpCrashScreenshotForDev_Count = 2 ///< Total values supported by this enum.
} NacpCrashScreenshotForDev;
typedef enum {
NacpContentsAvailabilityTransitionPolicy_NoPolicy = 0,
NacpContentsAvailabilityTransitionPolicy_Stable = 1,
NacpContentsAvailabilityTransitionPolicy_Changeable = 2,
NacpContentsAvailabilityTransitionPolicy_Count = 3, ///< Total values supported by this enum.
// Old.
NacpContentsAvailabilityTransitionPolicy_Legacy = NacpContentsAvailabilityTransitionPolicy_NoPolicy
} NacpContentsAvailabilityTransitionPolicy;
typedef struct {
u64 application_id[8];
} NacpAccessibleLaunchRequiredVersion;
NXDT_ASSERT(NacpAccessibleLaunchRequiredVersion, 0x40);
typedef struct {
NacpTitle title[0x10];
char isbn[0x25];
u8 startup_user_account; ///< NacpStartupUserAccount.
u8 user_account_switch_lock; ///< NacpUserAccountSwitchLock.
u8 add_on_content_registration_type; ///< NacpAddOnContentRegistrationType.
u32 attribute; ///< NacpAttribute.
u32 supported_language; ///< NacpSupportedLanguage.
u32 parental_control; ///< NacpParentalControl.
u8 screenshot; ///< NacpScreenshot.
u8 video_capture; ///< NacpVideoCapture.
u8 data_loss_confirmation; ///< NacpDataLossConfirmation.
u8 play_log_policy; ///< NacpPlayLogPolicy.
u64 presence_group_id;
NacpRatingAge rating_age;
char display_version[0x10];
u64 add_on_content_base_id;
u64 save_data_owner_id;
s64 user_account_save_data_size;
s64 user_account_save_data_journal_size;
s64 device_save_data_size;
s64 device_save_data_journal_size;
s64 bcat_delivery_cache_storage_size;
char application_error_code_category[0x8];
u64 local_communication_id[0x8];
u8 logo_type; ///< NacpLogoType.
u8 logo_handling; ///< NacpLogoHandling.
u8 runtime_add_on_content_install; ///< NacpRuntimeAddOnContentInstall.
u8 runtime_parameter_delivery; ///< NacpRuntimeParameterDelivery.
u8 appropriate_age_for_china; ///< NacpAppropriateAgeForChina.
u8 undecided_parameter_75b8b; ///< NacpUndecidedParameter75b8b.
u8 crash_report; ///< NacpCrashReport.
u8 hdcp; ///< NacpHdcp.
u64 seed_for_pseudo_device_id;
char bcat_passphrase[0x41];
u8 startup_user_account_option; ///< NacpStartupUserAccountOption.
u8 reserved_for_user_account_save_data_operation[0x6];
s64 user_account_save_data_size_max;
s64 user_account_save_data_journal_size_max;
s64 device_save_data_size_max;
s64 device_save_data_journal_size_max;
s64 temporary_storage_size;
s64 cache_storage_size;
s64 cache_storage_journal_size;
s64 cache_storage_data_and_journal_size_max;
u16 cache_storage_index_max;
u8 reserved_1;
u8 runtime_upgrade; ///< NacpRuntimeUpgrade.
u32 supporting_limited_application_licenses; ///< NacpSupportingLimitedApplicationLicenses.
u64 play_log_queryable_application_id[0x10];
u8 play_log_query_capability; ///< NacpPlayLogQueryCapability.
u8 repair; ///< NacpRepair.
u8 program_index;
u8 required_network_service_license_on_launch; ///< NacpRequiredNetworkServiceLicenseOnLaunch.
u8 reserved_2[0x4];
NacpNeighborDetectionClientConfiguration neighbor_detection_client_configuration;
NacpJitConfiguration jit_configuration;
NacpRequiredAddOnContentsSetBinaryDescriptor required_add_on_contents_set_binary_descriptor;
u8 play_report_permission; ///< NacpPlayReportPermission.
u8 crash_screenshot_for_prod; ///< NacpCrashScreenshotForProd.
u8 crash_screenshot_for_dev; ///< NacpCrashScreenshotForDev.
u8 contents_availability_transition_policy; ///< NacpContentsAvailabilityTransitionPolicy.
u8 reserved_3[0x4];
NacpAccessibleLaunchRequiredVersion accessible_launch_required_version;
u8 reserved_4[0xBB8];
} _NacpStruct;
NXDT_ASSERT(_NacpStruct, 0x4000);
typedef struct {
u8 language; ///< NacpLanguage.
u64 icon_size; ///< JPG icon size. Must not exceed NACP_MAX_ICON_SIZE.
u8 *icon_data; ///< Pointer to a dynamically allocated buffer that holds the JPG icon data.
} NacpIconContext;
typedef struct {
NcaContext *nca_ctx; ///< Pointer to the NCA context for the Control NCA from which NACP data is retrieved.
RomFileSystemContext romfs_ctx; ///< RomFileSystemContext for the Control NCA FS section #0, which is where the NACP is stored.
RomFileSystemFileEntry *romfs_file_entry; ///< RomFileSystemFileEntry for the NACP in the Control NCA FS section #0. Used to generate a RomFileSystemFileEntryPatch if needed.
RomFileSystemFileEntryPatch nca_patch; ///< RomFileSystemFileEntryPatch generated if NACP modifications are needed. Used to seamlessly replace Control NCA data while writing it.
///< Bear in mind that generating a patch modifies the NCA context.
_NacpStruct *data; ///< Pointer to a dynamically allocated buffer that holds the full NACP.
u8 data_hash[SHA256_HASH_SIZE]; ///< SHA-256 checksum calculated over the whole NACP. Used to determine if NcaHierarchicalSha256Patch generation is truly needed.
u8 icon_count; ///< NACP icon count. May be zero if no icons are available.
NacpIconContext *icon_ctx; ///< Pointer to a dynamically allocated buffer that holds 'icon_count' NACP icon contexts. May be NULL if no icons are available.
char *authoring_tool_xml; ///< Pointer to a dynamically allocated, NULL-terminated buffer that holds AuthoringTool-like XML data.
///< This is always NULL unless nacpGenerateAuthoringToolXml() is used on this NacpContext.
u64 authoring_tool_xml_size; ///< Size for the AuthoringTool-like XML. This is essentially the same as using strlen() on 'authoring_tool_xml'.
///< This is always 0 unless nacpGenerateAuthoringToolXml() is used on this NacpContext.
} NacpContext;
/// Initializes a NacpContext using a previously initialized NcaContext (which must belong to a Control NCA).
bool nacpInitializeContext(NacpContext *out, NcaContext *nca_ctx);
/// Changes flags in the NACP from the input NacpContext and generates a RomFS file entry patch if needed.
/// If 'patch_sua' is true, StartupUserAccount is set to None, the IsOptional bit in StartupUserAccountOption is cleared and UserAccountSwitchLock is set to Disable.
/// If 'patch_screenshot' is true, Screenshot is set to Allow.
/// If 'patch_video_capture' is true, VideoCapture is set to Enable.
/// If 'patch_hdcp' is true, Hdcp is set to None.
bool nacpGenerateNcaPatch(NacpContext *nacp_ctx, bool patch_sua, bool patch_screenshot, bool patch_video_capture, bool patch_hdcp);
/// Writes data from the RomFS file entry patch in the input NacpContext to the provided buffer.
void nacpWriteNcaPatch(NacpContext *nacp_ctx, void *buf, u64 buf_size, u64 buf_offset);
/// Generates an AuthoringTool-like XML using information from a previously initialized NacpContext, as well as the Application/Patch version and the required system version.
/// If the function succeeds, XML data and size will get saved to the 'authoring_tool_xml' and 'authoring_tool_xml_size' members from the NacpContext.
bool nacpGenerateAuthoringToolXml(NacpContext *nacp_ctx, u32 version, u32 required_system_version);
/// These functions return pointers to string representations of the input flag/value index (e.g. nacpGetLanguageString(NacpLanguage_AmericanEnglish) -> "AmericanEnglish").
/// If the input flag/value index is invalid, "Unknown" will be returned.
/// If dealing with a bitflag field such as:
/// * NacpAttribute
/// * NacpSupportedLanguage
/// * NacpParentalControl
/// * NacpStartupUserAccountOption
/// * NacpRepair
/// Then, the provided value must be a 0-based index to the desired flag and not a bitmask from its enum (e.g. NacpAttribute_RetailInteractiveDisplay -> use 1 instead).
const char *nacpGetLanguageString(u8 language); /// Can also be used for NacpSupportedLanguage flags with values from the NacpLanguage enum.
const char *nacpGetStartupUserAccountString(u8 startup_user_account);
const char *nacpGetUserAccountSwitchLockString(u8 user_account_switch_lock);
const char *nacpGetAddOnContentRegistrationTypeString(u8 add_on_content_registration_type);
const char *nacpGetAttributeString(u8 attribute);
const char *nacpGetParentalControlString(u8 parental_control);
const char *nacpGetScreenshotString(u8 screenshot);
const char *nacpGetVideoCaptureString(u8 video_capture);
const char *nacpGetDataLossConfirmationString(u8 data_loss_confirmation);
const char *nacpGetPlayLogPolicyString(u8 play_log_policy);
const char *nacpGetRatingAgeOrganizationString(u8 rating_age_organization);
const char *nacpGetLogoTypeString(u8 logo_type);
const char *nacpGetLogoHandlingString(u8 logo_handling);
const char *nacpGetRuntimeAddOnContentInstallString(u8 runtime_add_on_content_install);
const char *nacpGetRuntimeParameterDeliveryString(u8 runtime_parameter_delivery);
const char *nacpGetAppropriateAgeForChina(u8 appropriate_age_for_china);
const char *nacpGetUndecidedParameter75b8bString(u8 undecided_parameter_75b8b);
const char *nacpGetCrashReportString(u8 crash_report);
const char *nacpGetHdcpString(u8 hdcp);
const char *nacpGetStartupUserAccountOptionString(u8 startup_user_account_option);
const char *nacpGetRuntimeUpgradeString(u8 runtime_upgrade);
const char *nacpGetPlayLogQueryCapabilityString(u8 play_log_query_capability);
const char *nacpGetRepairString(u8 repair);
const char *nacpGetRequiredNetworkServiceLicenseOnLaunchString(u8 required_network_service_license_on_launch);
const char *nacpGetCrashScreenshotForProdString(u8 crash_screenshot_for_prod);
const char *nacpGetCrashScreenshotForDevString(u8 crash_screenshot_for_dev);
const char *nacpGetContentsAvailabilityTransitionPolicyString(u8 contents_availability_transition_policy);
/// Helper inline functions.
NX_INLINE void nacpFreeContext(NacpContext *nacp_ctx)
{
if (!nacp_ctx) return;
romfsFreeContext(&(nacp_ctx->romfs_ctx));
romfsFreeFileEntryPatch(&(nacp_ctx->nca_patch));
if (nacp_ctx->data) free(nacp_ctx->data);
if (nacp_ctx->icon_ctx)
{
for(u8 i = 0; i < nacp_ctx->icon_count; i++)
{
if (nacp_ctx->icon_ctx[i].icon_data) free(nacp_ctx->icon_ctx[i].icon_data);
}
free(nacp_ctx->icon_ctx);
}
if (nacp_ctx->authoring_tool_xml) free(nacp_ctx->authoring_tool_xml);
memset(nacp_ctx, 0, sizeof(NacpContext));
}
NX_INLINE bool nacpIsValidIconContext(NacpIconContext *icon_ctx)
{
return (icon_ctx && icon_ctx->language < NacpLanguage_Count && icon_ctx->icon_size && icon_ctx->icon_data);
}
NX_INLINE bool nacpIsValidContext(NacpContext *nacp_ctx)
{
if (!nacp_ctx || !nacp_ctx->nca_ctx || !nacp_ctx->romfs_file_entry || !nacp_ctx->data || (!nacp_ctx->icon_count && nacp_ctx->icon_ctx) || (nacp_ctx->icon_count && !nacp_ctx->icon_ctx)) return false;
for(u8 i = 0; i < nacp_ctx->icon_count; i++)
{
if (!nacpIsValidIconContext(&(nacp_ctx->icon_ctx[i]))) return false;
}
return true;
}
#ifdef __cplusplus
}
#endif
#endif /* __NACP_H__ */

613
include/core/nca.h Normal file
View File

@ -0,0 +1,613 @@
/*
* nca.h
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __NCA_H__
#define __NCA_H__
#include "tik.h"
#ifdef __cplusplus
extern "C" {
#endif
#define NCA_FS_HEADER_COUNT 4
#define NCA_FULL_HEADER_LENGTH (sizeof(NcaHeader) + (sizeof(NcaFsHeader) * NCA_FS_HEADER_COUNT))
#define NCA_NCA0_MAGIC 0x4E434130 /* "NCA0". */
#define NCA_NCA2_MAGIC 0x4E434132 /* "NCA2". */
#define NCA_NCA3_MAGIC 0x4E434133 /* "NCA3". */
#define NCA_KEY_AREA_KEY_COUNT 0x10
#define NCA_KEY_AREA_SIZE (NCA_KEY_AREA_KEY_COUNT * AES_128_KEY_SIZE)
#define NCA_KEY_AREA_USED_KEY_COUNT 3
#define NCA_KEY_AREA_USED_SIZE (NCA_KEY_AREA_USED_KEY_COUNT * AES_128_KEY_SIZE)
#define NCA_HIERARCHICAL_SHA256_MAX_REGION_COUNT 5
#define NCA_IVFC_MAGIC 0x49564643 /* "IVFC". */
#define NCA_IVFC_MAX_LEVEL_COUNT 7
#define NCA_IVFC_LEVEL_COUNT (NCA_IVFC_MAX_LEVEL_COUNT - 1)
#define NCA_IVFC_BLOCK_SIZE(x) (1U << (x))
#define NCA_BKTR_MAGIC 0x424B5452 /* "BKTR". */
#define NCA_BKTR_VERSION 1
#define NCA_FS_SECTOR_SIZE 0x200
#define NCA_FS_SECTOR_OFFSET(x) ((u64)(x) * NCA_FS_SECTOR_SIZE)
#define NCA_AES_XTS_SECTOR_SIZE 0x200
#define NCA_SIGNATURE_AREA_SIZE 0x200 /* Signature is calculated starting at the NCA header magic word. */
typedef enum {
NcaDistributionType_Download = 0,
NcaDistributionType_GameCard = 1,
NcaDistributionType_Count = 2 ///< Total values supported by this enum.
} NcaDistributionType;
typedef enum {
NcaContentType_Program = 0,
NcaContentType_Meta = 1,
NcaContentType_Control = 2,
NcaContentType_Manual = 3,
NcaContentType_Data = 4,
NcaContentType_PublicData = 5,
NcaContentType_Count = 6 ///< Total values supported by this enum.
} NcaContentType;
/// 'NcaKeyGeneration_Current' will always point to the last known key generation value.
/// TODO: update on master key changes.
typedef enum {
NcaKeyGeneration_Since100NUP = 0, ///< 1.0.0 - 2.3.0.
NcaKeyGeneration_Since300NUP = 2, ///< 3.0.0.
NcaKeyGeneration_Since301NUP = 3, ///< 3.0.1 - 3.0.2.
NcaKeyGeneration_Since400NUP = 4, ///< 4.0.0 - 4.1.0.
NcaKeyGeneration_Since500NUP = 5, ///< 5.0.0 - 5.1.0.
NcaKeyGeneration_Since600NUP = 6, ///< 6.0.0 - 6.1.0.
NcaKeyGeneration_Since620NUP = 7, ///< 6.2.0.
NcaKeyGeneration_Since700NUP = 8, ///< 7.0.0 - 8.0.1.
NcaKeyGeneration_Since810NUP = 9, ///< 8.1.0 - 8.1.1.
NcaKeyGeneration_Since900NUP = 10, ///< 9.0.0 - 9.0.1.
NcaKeyGeneration_Since910NUP = 11, ///< 9.1.0 - 12.0.3.
NcaKeyGeneration_Since1210NUP = 12, ///< 12.1.0.
NcaKeyGeneration_Since1300NUP = 13, ///< 13.0.0 - 13.2.1.
NcaKeyGeneration_Since1400NUP = 14, ///< 14.0.0 - 14.1.2.
NcaKeyGeneration_Since1500NUP = 15, ///< 15.0.0 - 15.0.1.
NcaKeyGeneration_Since1600NUP = 16, ///< 16.0.0 - 16.1.0.
NcaKeyGeneration_Since1700NUP = 17, ///< 17.0.0 - 17.0.1.
NcaKeyGeneration_Since1800NUP = 18, ///< 18.0.0+.
NcaKeyGeneration_Current = NcaKeyGeneration_Since1800NUP,
NcaKeyGeneration_Max = 32
} NcaKeyGeneration;
typedef enum {
NcaKeyAreaEncryptionKeyIndex_Application = 0,
NcaKeyAreaEncryptionKeyIndex_Ocean = 1,
NcaKeyAreaEncryptionKeyIndex_System = 2,
NcaKeyAreaEncryptionKeyIndex_Count = 3 ///< Total values supported by this enum.
} NcaKeyAreaEncryptionKeyIndex;
/// 'NcaSignatureKeyGeneration_Current' will always point to the last known key generation value.
/// TODO: update on signature keygen changes.
typedef enum {
NcaSignatureKeyGeneration_Since100NUP = 0, ///< 1.0.0 - 8.1.1.
NcaSignatureKeyGeneration_Since900NUP = 1, ///< 9.0.0+.
NcaSignatureKeyGeneration_Current = NcaSignatureKeyGeneration_Since900NUP,
NcaSignatureKeyGeneration_Max = (NcaSignatureKeyGeneration_Current + 1)
} NcaSignatureKeyGeneration;
typedef struct {
u32 start_sector; ///< Expressed in NCA_FS_SECTOR_SIZE sectors.
u32 end_sector; ///< Expressed in NCA_FS_SECTOR_SIZE sectors.
u32 hash_sector;
u8 reserved[0x4];
} NcaFsInfo;
NXDT_ASSERT(NcaFsInfo, 0x10);
typedef struct {
u8 hash[SHA256_HASH_SIZE];
} NcaFsHeaderHash;
NXDT_ASSERT(NcaFsHeaderHash, 0x20);
/// Encrypted NCA key area used to hold NCA FS section encryption keys. Zeroed out if the NCA uses titlekey crypto.
/// If a particular key entry is unused, it is zeroed out before this area is encrypted.
typedef struct {
union {
u8 keys[NCA_KEY_AREA_KEY_COUNT][AES_128_KEY_SIZE];
struct {
u8 aes_xts_1[AES_128_KEY_SIZE]; ///< AES-128-XTS key 0 used for NCA FS sections with NcaEncryptionType_AesXts crypto.
u8 aes_xts_2[AES_128_KEY_SIZE]; ///< AES-128-XTS key 1 used for NCA FS sections with NcaEncryptionType_AesXts crypto.
u8 aes_ctr[AES_128_KEY_SIZE]; ///< AES-128-CTR key used for NCA FS sections with NcaEncryptionType_AesCtr* and NcaEncryptionType_AesCtrEx* crypto.
u8 aes_ctr_ex[AES_128_KEY_SIZE]; ///< Unused AES-128-CTR key.
u8 aes_ctr_hw[AES_128_KEY_SIZE]; ///< Unused AES-128-CTR key.
u8 reserved[0xB0];
};
};
} NcaEncryptedKeyArea;
NXDT_ASSERT(NcaEncryptedKeyArea, NCA_KEY_AREA_SIZE);
/// First 0x400 bytes from every NCA.
typedef struct {
u8 main_signature[0x100]; ///< RSA-2048-PSS with SHA-256 signature over header using a fixed key.
u8 acid_signature[0x100]; ///< RSA-2048-PSS with SHA-256 signature over header using the ACID public key from the NPDM in ExeFS. Only used in Program NCAs.
u32 magic; ///< "NCA0" / "NCA2" / "NCA3".
u8 distribution_type; ///< NcaDistributionType.
u8 content_type; ///< NcaContentType.
u8 key_generation_old; ///< NcaKeyGeneration. Only uses NcaKeyGeneration_Since100NUP and NcaKeyGeneration_Since300NUP values.
u8 kaek_index; ///< NcaKeyAreaEncryptionKeyIndex.
u64 content_size;
u64 program_id;
u32 content_index;
Version sdk_addon_version;
u8 key_generation; ///< NcaKeyGeneration. Uses NcaKeyGeneration_Since301NUP or greater values.
u8 main_signature_key_generation; ///< NcaSignatureKeyGeneration.
u8 reserved[0xE];
FsRightsId rights_id; ///< Used for titlekey crypto.
NcaFsInfo fs_info[NCA_FS_HEADER_COUNT]; ///< Start and end sectors for each NCA FS section.
NcaFsHeaderHash fs_header_hash[NCA_FS_HEADER_COUNT]; ///< SHA-256 hashes calculated over each NCA FS section header.
NcaEncryptedKeyArea encrypted_key_area;
} NcaHeader;
NXDT_ASSERT(NcaHeader, 0x400);
typedef enum {
NcaFsType_RomFs = 0,
NcaFsType_PartitionFs = 1,
NcaFsType_Count = 2 ///< Total values supported by this enum.
} NcaFsType;
typedef enum {
NcaHashType_Auto = 0,
NcaHashType_None = 1, ///< Possibly used by all filesystem types.
NcaHashType_HierarchicalSha256 = 2, ///< Used by NcaFsType_PartitionFs.
NcaHashType_HierarchicalIntegrity = 3, ///< Used by NcaFsType_RomFs.
NcaHashType_AutoSha3 = 4,
NcaHashType_HierarchicalSha3256 = 5, ///< Used by NcaFsType_PartitionFs.
NcaHashType_HierarchicalIntegritySha3 = 6, ///< Used by NcaFsType_RomFs.
NcaHashType_Count = 7 ///< Total values supported by this enum.
} NcaHashType;
typedef enum {
NcaEncryptionType_Auto = 0,
NcaEncryptionType_None = 1,
NcaEncryptionType_AesXts = 2,
NcaEncryptionType_AesCtr = 3,
NcaEncryptionType_AesCtrEx = 4,
NcaEncryptionType_AesCtrSkipLayerHash = 5,
NcaEncryptionType_AesCtrExSkipLayerHash = 6,
NcaEncryptionType_Count = 7 ///< Total values supported by this enum.
} NcaEncryptionType;
typedef enum {
NcaMetaDataHashType_None = 0,
NcaMetaDataHashType_HierarchicalIntegrity = 1,
NcaMetaDataHashType_HierarchicalIntegritySha3 = 2,
NcaMetaDataHashType_Count = 3 ///< Total values supported by this enum.
} NcaMetaDataHashType;
typedef struct {
u64 offset;
u64 size;
} NcaRegion;
NXDT_ASSERT(NcaRegion, 0x10);
/// Used by NcaFsType_PartitionFs and NCA0 NcaFsType_RomFs.
typedef struct {
u8 master_hash[SHA256_HASH_SIZE];
u32 hash_block_size;
u32 hash_region_count;
NcaRegion hash_region[NCA_HIERARCHICAL_SHA256_MAX_REGION_COUNT];
} NcaHierarchicalSha256Data;
NXDT_ASSERT(NcaHierarchicalSha256Data, 0x78);
typedef struct {
u64 offset;
u64 size;
u32 block_order; ///< Use NCA_IVFC_BLOCK_SIZE to calculate the actual block size using this value.
u8 reserved[0x4];
} NcaHierarchicalIntegrityVerificationLevelInformation;
NXDT_ASSERT(NcaHierarchicalIntegrityVerificationLevelInformation, 0x18);
typedef struct {
u8 value[0x20];
} NcaSignatureSalt;
NXDT_ASSERT(NcaSignatureSalt, 0x20);
#pragma pack(push, 1)
typedef struct {
u32 max_level_count; ///< Always NCA_IVFC_MAX_LEVEL_COUNT.
NcaHierarchicalIntegrityVerificationLevelInformation level_information[NCA_IVFC_LEVEL_COUNT];
NcaSignatureSalt signature_salt;
} NcaInfoLevelHash;
#pragma pack(pop)
NXDT_ASSERT(NcaInfoLevelHash, 0xB4);
/// Used by NcaFsType_RomFs.
typedef struct {
u32 magic; ///< "IVFC".
u32 version;
u32 master_hash_size; ///< Always SHA256_HASH_SIZE.
NcaInfoLevelHash info_level_hash;
u8 master_hash[SHA256_HASH_SIZE];
} NcaIntegrityMetaInfo;
NXDT_ASSERT(NcaIntegrityMetaInfo, 0xE0);
typedef struct {
union {
struct {
///< Used for NcaHashType_HierarchicalSha256 and NcaHashType_HierarchicalSha3256 (NcaFsType_PartitionFs and NCA0 NcaFsType_RomFs).
NcaHierarchicalSha256Data hierarchical_sha256_data;
u8 reserved_1[0x80];
};
struct {
///< Used if NcaHashType_HierarchicalIntegrity and NcaHashType_HierarchicalIntegritySha3 (NcaFsType_RomFs).
NcaIntegrityMetaInfo integrity_meta_info;
u8 reserved_2[0x18];
};
};
} NcaHashData;
NXDT_ASSERT(NcaHashData, 0xF8);
typedef struct {
u32 magic; ///< "BKTR".
u32 version; ///< Always NCA_BKTR_VERSION.
u32 entry_count;
u8 reserved[0x4];
} NcaBucketTreeHeader;
NXDT_ASSERT(NcaBucketTreeHeader, 0x10);
typedef struct {
u64 offset;
u64 size;
NcaBucketTreeHeader header;
} NcaBucketInfo;
NXDT_ASSERT(NcaBucketInfo, 0x20);
/// Only used for NcaEncryptionType_AesCtrEx and NcaEncryptionType_AesCtrExSkipLayerHash (PatchRomFs).
typedef struct {
NcaBucketInfo indirect_bucket;
NcaBucketInfo aes_ctr_ex_bucket;
} NcaPatchInfo;
NXDT_ASSERT(NcaPatchInfo, 0x40);
typedef struct {
union {
u8 value[0x8];
struct {
u32 generation;
u32 secure_value;
};
};
} NcaAesCtrUpperIv;
NXDT_ASSERT(NcaAesCtrUpperIv, 0x8);
/// Used in NCAs with sparse storage.
typedef struct {
NcaBucketInfo bucket;
u64 physical_offset;
u16 generation;
u8 reserved[0x6];
} NcaSparseInfo;
NXDT_ASSERT(NcaSparseInfo, 0x30);
/// Used in NCAs with LZ4-compressed sections.
typedef struct {
NcaBucketInfo bucket;
u8 reserved[0x8];
} NcaCompressionInfo;
NXDT_ASSERT(NcaCompressionInfo, 0x28);
typedef struct {
u64 offset;
u64 size;
u8 hash[SHA256_HASH_SIZE];
} NcaMetaDataHashDataInfo;
NXDT_ASSERT(NcaMetaDataHashDataInfo, 0x30);
/// Four NCA FS headers are placed right after the 0x400 byte long NCA header in NCA2 and NCA3.
/// NCA0 place the FS headers at the start sector from the NcaFsInfo entries.
typedef struct {
u16 version;
u8 fs_type; ///< NcaFsType.
u8 hash_type; ///< NcaHashType.
u8 encryption_type; ///< NcaEncryptionType.
u8 metadata_hash_type; ///< NcaMetaDataHashType.
u8 reserved_1[0x2];
NcaHashData hash_data;
NcaPatchInfo patch_info;
NcaAesCtrUpperIv aes_ctr_upper_iv;
NcaSparseInfo sparse_info;
NcaCompressionInfo compression_info;
NcaMetaDataHashDataInfo metadata_hash_info;
u8 reserved_2[0x30];
} NcaFsHeader;
NXDT_ASSERT(NcaFsHeader, 0x200);
typedef enum {
NcaFsSectionType_PartitionFs = 0, ///< NcaFsType_PartitionFs + NcaHashType_HierarchicalSha256 OR NcaHashType_HierarchicalSha3256 + NcaEncryptionType_AesCtr OR NcaEncryptionType_AesCtrSkipLayerHash.
NcaFsSectionType_RomFs = 1, ///< NcaFsType_RomFs + NcaHashType_HierarchicalIntegrity OR NcaHashType_HierarchicalIntegritySha3 + NcaEncryptionType_AesCtr OR NcaEncryptionType_AesCtrSkipLayerHash.
NcaFsSectionType_PatchRomFs = 2, ///< NcaFsType_RomFs + NcaHashType_HierarchicalIntegrity OR NcaHashType_HierarchicalIntegritySha3 + NcaEncryptionType_AesCtrEx OR NcaEncryptionType_AesCtrExSkipLayerHash.
NcaFsSectionType_Nca0RomFs = 3, ///< NcaVersion_Nca0 + NcaFsType_RomFs + NcaHashType_HierarchicalSha256 + NcaEncryptionType_AesXts.
NcaFsSectionType_Invalid = 4
} NcaFsSectionType;
// Forward declaration for NcaFsSectionContext.
typedef struct _NcaContext NcaContext;
/// Unlike NCA contexts, we don't need to keep a hash for the NCA FS section header in NCA FS section contexts.
/// This is because the functions that modify the NCA FS section header also update the NCA FS section header hash stored in the NCA header.
typedef struct {
bool enabled; ///< Set to true if this NCA FS section has passed all validation checks and can be safely used.
NcaContext *nca_ctx; ///< NcaContext. Used to perform NCA reads.
NcaFsHeader header; ///< Plaintext NCA FS section header.
NcaFsHeader encrypted_header; ///< Encrypted NCA FS section header. If the plaintext NCA FS section header is modified, this will hold an encrypted copy of it.
///< Otherwise, this holds the unmodified, encrypted NCA FS section header.
u8 section_idx; ///< Index within [0, NCA_FS_HEADER_COUNT).
u64 section_offset; ///< Relative to the start of the NCA content file. Placed here for convenience.
u64 section_size; ///< Placed here for convenience.
char section_size_str[0x10]; ///< Placed here for convenience.
u8 hash_type; ///< NcaHashType.
u8 encryption_type; ///< NcaEncryptionType.
u8 section_type; ///< NcaFsSectionType.
///< PatchInfo-related fields.
bool has_patch_indirect_layer; ///< Set to true if this NCA FS section has an Indirect patch layer.
bool has_patch_aes_ctr_ex_layer; ///< Set to true if this NCA FS section has an AesCtrEx patch layer.
///< SparseInfo-related fields.
bool has_sparse_layer; ///< Set to true if this NCA FS section has a sparse layer.
u64 sparse_table_offset; ///< header.sparse_info.physical_offset + header.sparse_info.bucket.offset. Relative to the start of the NCA content file. Placed here for convenience.
u64 cur_sparse_virtual_offset; ///< Current sparse layer virtual offset. Used for content decryption if a sparse layer is available.
///< CompressionInfo-related fields.
bool has_compression_layer; ///< Set to true if this NCA FS section has a compression layer.
///< Hash-layer-related fields.
bool skip_hash_layer_crypto; ///< Set to true if hash layer crypto should be skipped while reading section data.
NcaRegion hash_region; ///< Holds the properties for the full hash layer region that precedes the actual FS section data.
///< Crypto-related fields.
u8 ctr[AES_BLOCK_SIZE]; ///< Used internally by NCA functions to update the AES-128-CTR context IV based on the desired offset.
Aes128CtrContext ctr_ctx; ///< Used internally by NCA functions to perform AES-128-CTR crypto.
Aes128XtsContext xts_decrypt_ctx; ///< Used internally by NCA functions to perform AES-128-XTS decryption.
Aes128XtsContext xts_encrypt_ctx; ///< Used internally by NCA functions to perform AES-128-XTS encryption.
///< NSP-related fields.
bool header_written; ///< Set to true after this FS section header has been written to an output dump.
} NcaFsSectionContext;
typedef enum {
NcaVersion_Nca0 = 0,
NcaVersion_Nca2 = 1,
NcaVersion_Nca3 = 2,
NcaVersion_Count = 3 ///< Total values supported by this enum.
} NcaVersion;
typedef struct {
union {
u8 keys[NCA_KEY_AREA_USED_KEY_COUNT][AES_128_KEY_SIZE];
struct {
u8 aes_xts_1[AES_128_KEY_SIZE]; ///< AES-128-XTS key 0 used for NCA FS sections with NcaEncryptionType_AesXts crypto.
u8 aes_xts_2[AES_128_KEY_SIZE]; ///< AES-128-XTS key 1 used for NCA FS sections with NcaEncryptionType_AesXts crypto.
u8 aes_ctr[AES_128_KEY_SIZE]; ///< AES-128-CTR key used for NCA FS sections with NcaEncryptionType_AesCtr and NcaEncryptionType_AesCtrSkipLayerHash crypto.
};
};
} NcaDecryptedKeyArea;
NXDT_ASSERT(NcaDecryptedKeyArea, NCA_KEY_AREA_USED_SIZE);
struct _NcaContext {
u8 storage_id; ///< NcmStorageId.
NcmContentStorage *ncm_storage; ///< Pointer to a NcmContentStorage instance. Used to read NCA data from eMMC/SD.
u64 gamecard_offset; ///< Used to read NCA data from a gamecard using a FsStorage instance when storage_id == NcmStorageId_GameCard.
u64 title_id; ///< ID from the title that owns this NCA. Retrieved from NcmContentMetaKey. Placed here for convenience.
Version title_version; ///< Version from the title that owns this NCA. Retrieved from NcmContentMetaKey. Placed here for convenience.
u8 title_type; ///< NcmContentMetaType. Retrieved from NcmContentMetaKey. Placed here for convenience.
NcmContentId content_id; ///< Content ID for this NCA. Used to read NCA data from eMMC/SD. Retrieved from NcmContentInfo.
char content_id_str[0x21];
u8 hash[SHA256_HASH_SIZE]; ///< Manually calculated (if needed).
char hash_str[SHA256_HASH_STR_SIZE];
u8 format_version; ///< NcaVersion.
u8 content_type; ///< NcmContentType. Retrieved from NcmContentInfo.
u64 content_size; ///< Retrieved from NcmContentInfo.
char content_size_str[0x10]; ///< Placed here for convenience.
u8 key_generation; ///< NcaKeyGeneration. Retrieved from the decrypted header.
u8 id_offset; ///< Retrieved from NcmContentInfo.
bool rights_id_available;
bool titlekey_retrieved;
bool valid_main_signature;
u8 titlekey[AES_128_KEY_SIZE]; ///< Decrypted titlekey from the ticket.
NcaHeader header; ///< Plaintext NCA header.
u8 header_hash[SHA256_HASH_SIZE]; ///< Plaintext NCA header hash. Used to determine if it's necessary to replace the NCA header while dumping this NCA.
NcaHeader encrypted_header; ///< Encrypted NCA header. If the plaintext NCA header is modified, this will hold an encrypted copy of it.
///< Otherwise, this holds the unmodified, encrypted NCA header.
NcaDecryptedKeyArea decrypted_key_area;
NcaFsSectionContext fs_ctx[NCA_FS_HEADER_COUNT];
///< NSP-related fields.
bool header_written; ///< Set to true after the NCA header and the FS section headers have been written to an output dump.
void *content_type_ctx; ///< Pointer to a content type context (e.g. ContentMetaContext, ProgramInfoContext, NacpContext, LegalInfoContext). Set to NULL if unused.
bool content_type_ctx_patch; ///< Set to true if a NCA patch generated by the content type context is needed and hasn't been completely written yet.
u32 content_type_ctx_data_idx; ///< Start index for the data generated by the content type context. Used while creating NSPs.
};
typedef struct {
bool written; ///< Set to true if this patch has already been written.
u64 offset; ///< New data offset (relative to the start of the NCA content file).
u64 size; ///< New data size.
u8 *data; ///< New data.
} NcaHashDataPatch;
typedef struct {
bool written; ///< Set to true if all hash region patches have been written.
NcmContentId content_id;
u32 hash_region_count;
NcaHashDataPatch hash_region_patch[NCA_HIERARCHICAL_SHA256_MAX_REGION_COUNT];
} NcaHierarchicalSha256Patch;
typedef struct {
bool written; ///< Set to true if all hash level patches have been written.
NcmContentId content_id;
NcaHashDataPatch hash_level_patch[NCA_IVFC_LEVEL_COUNT];
} NcaHierarchicalIntegrityPatch;
/// Functions to control the internal heap buffer used by NCA FS section crypto operations.
/// Must be called at startup.
bool ncaAllocateCryptoBuffer(void);
void ncaFreeCryptoBuffer(void);
/// Initializes a NCA context.
/// If 'storage_id' == NcmStorageId_GameCard, the 'hfs_partition_type' argument must be a valid HashFileSystemPartitionType value.
/// If the NCA holds a populated Rights ID field, ticket data will need to be retrieved.
/// If the 'tik' argument points to a valid Ticket element, it will either be updated (if it's empty) or used to read ticket data that has already been retrieved.
/// If the 'tik' argument is NULL, the function will just retrieve the necessary ticket data on its own.
/// If ticket data can't be retrieved, the context will still be initialized, but anything that involves working with encrypted NCA FS section blocks won't be possible (e.g. ncaReadFsSection()).
bool ncaInitializeContext(NcaContext *out, u8 storage_id, u8 hfs_partition_type, const NcmContentMetaKey *meta_key, const NcmContentInfo *content_info, Ticket *tik);
/// Reads raw encrypted data from a NCA using an input context, previously initialized by ncaInitializeContext().
/// Input offset must be relative to the start of the NCA content file.
bool ncaReadContentFile(NcaContext *ctx, void *out, u64 read_size, u64 offset);
/// Retrieves the FS section's hierarchical hash target layer extents.
/// Output offset is relative to the start of the FS section.
/// Either 'out_offset' or 'out_size' can be NULL, but at least one of them must be a valid pointer.
bool ncaGetFsSectionHashTargetExtents(NcaFsSectionContext *ctx, u64 *out_offset, u64 *out_size);
/// Reads decrypted data from a NCA FS section using an input context.
/// Input offset must be relative to the start of the NCA FS section.
/// If dealing with Patch RomFS sections, this function should only be used when *not* reading AesCtrEx storage data. Use ncaReadAesCtrExStorage() for that.
bool ncaReadFsSection(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset);
/// Reads plaintext AesCtrEx storage data from a NCA Patch RomFS section using an input context and an AesCtrEx CTR value.
/// Input offset must be relative to the start of the NCA FS section.
bool ncaReadAesCtrExStorage(NcaFsSectionContext *ctx, void *out, u64 read_size, u64 offset, u32 ctr_val, bool decrypt);
/// Generates HierarchicalSha256 FS section patch data, which can be used to seamlessly replace NCA data.
/// Input offset must be relative to the start of the last HierarchicalSha256 hash region (actual underlying FS).
/// Bear in mind that this function recalculates both the NcaHashData block master hash and the NCA FS header hash from the NCA header.
/// As such, this function is not designed to generate more than one patch per HierarchicalSha256 FS section.
bool ncaGenerateHierarchicalSha256Patch(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, NcaHierarchicalSha256Patch *out);
/// Overwrites block(s) from a buffer holding raw NCA data using previously initialized NcaContext and NcaHierarchicalSha256Patch.
/// 'buf_offset' must hold the raw NCA offset where the data stored in 'buf' was read from.
/// The 'written' fields from the input NcaHierarchicalSha256Patch and its underlying NcaHashDataPatch elements are updated by this function.
void ncaWriteHierarchicalSha256PatchToMemoryBuffer(NcaContext *ctx, NcaHierarchicalSha256Patch *patch, void *buf, u64 buf_size, u64 buf_offset);
/// Generates HierarchicalIntegrity FS section patch data, which can be used to seamlessly replace NCA data.
/// Input offset must be relative to the start of the last HierarchicalIntegrity hash level (actual underlying FS).
/// Bear in mind that this function recalculates both the NcaHashData block master hash and the NCA FS header hash from the NCA header.
/// As such, this function is not designed to generate more than one patch per HierarchicalIntegrity FS section.
bool ncaGenerateHierarchicalIntegrityPatch(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, NcaHierarchicalIntegrityPatch *out);
/// Overwrites block(s) from a buffer holding raw NCA data using a previously initialized NcaContext and NcaHierarchicalIntegrityPatch.
/// 'buf_offset' must hold the raw NCA offset where the data stored in 'buf' was read from.
/// The 'written' fields from the input NcaHierarchicalIntegrityPatch and its underlying NcaHashDataPatch elements are updated by this function.
void ncaWriteHierarchicalIntegrityPatchToMemoryBuffer(NcaContext *ctx, NcaHierarchicalIntegrityPatch *patch, void *buf, u64 buf_size, u64 buf_offset);
/// Sets the distribution type field from the underlying NCA header in the provided NCA context to NcaDistributionType_Download.
/// Needed for NSP dumps from gamecard titles.
void ncaSetDownloadDistributionType(NcaContext *ctx);
/// Removes titlekey crypto dependency from a NCA context by wiping the Rights ID from the underlying NCA header and copying the decrypted titlekey to the NCA key area.
bool ncaRemoveTitleKeyCrypto(NcaContext *ctx);
/// Encrypts NCA header and NCA FS headers.
/// The 'encrypted_header' member from the NCA context and its underlying NCA FS section contexts is updated by this function.
/// Internally uses ncaIsHeaderDirty() to determine if NCA header / NCA FS section header re-encryption is needed.
bool ncaEncryptHeader(NcaContext *ctx);
/// Used to replace the NCA header and the NCA FS section headers while writing a NCA if they were modified in any way.
/// Overwrites block(s) from a buffer holding raw NCA data using a previously initialized NcaContext.
/// 'buf_offset' must hold the raw NCA offset where the data stored in 'buf' was read from.
/// Bear in mind this function doesn't call ncaIsHeaderDirty() on its own to avoid taking up too much execution time, so it will attempt to overwrite data even if it isn't needed.
void ncaWriteEncryptedHeaderDataToMemoryBuffer(NcaContext *ctx, void *buf, u64 buf_size, u64 buf_offset);
/// Updates the content ID and hash from a NCA context using a provided SHA-256 checksum.
void ncaUpdateContentIdAndHash(NcaContext *ctx, const u8 *hash);
/// Returns a pointer to a string holding the name of the section type from the provided NCA FS section context.
const char *ncaGetFsSectionTypeName(NcaFsSectionContext *ctx);
/// Helper inline functions.
NX_INLINE bool ncaIsHeaderDirty(NcaContext *ctx)
{
if (!ctx) return false;
u8 tmp_hash[SHA256_HASH_SIZE] = {0};
sha256CalculateHash(tmp_hash, &(ctx->header), sizeof(NcaHeader));
return (memcmp(tmp_hash, ctx->header_hash, SHA256_HASH_SIZE) != 0);
}
NX_INLINE bool ncaVerifyBucketInfo(NcaBucketInfo *bucket)
{
return (bucket && __builtin_bswap32(bucket->header.magic) == NCA_BKTR_MAGIC && bucket->header.version <= NCA_BKTR_VERSION && bucket->header.entry_count >= 0);
}
NX_INLINE void ncaFreeHierarchicalSha256Patch(NcaHierarchicalSha256Patch *patch)
{
if (!patch) return;
for(u32 i = 0; i < NCA_HIERARCHICAL_SHA256_MAX_REGION_COUNT; i++)
{
if (patch->hash_region_patch[i].data) free(patch->hash_region_patch[i].data);
}
memset(patch, 0, sizeof(NcaHierarchicalSha256Patch));
}
NX_INLINE void ncaFreeHierarchicalIntegrityPatch(NcaHierarchicalIntegrityPatch *patch)
{
if (!patch) return;
for(u32 i = 0; i < NCA_IVFC_LEVEL_COUNT; i++)
{
if (patch->hash_level_patch[i].data) free(patch->hash_level_patch[i].data);
}
memset(patch, 0, sizeof(NcaHierarchicalIntegrityPatch));
}
#ifdef __cplusplus
}
#endif
#endif /* __NCA_H__ */

View File

@ -0,0 +1,83 @@
/*
* nca_storage.h
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __NCA_STORAGE_H__
#define __NCA_STORAGE_H__
#include "bktr.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
NcaStorageBaseStorageType_Invalid = 0, ///< Placeholder.
NcaStorageBaseStorageType_Regular = 1,
NcaStorageBaseStorageType_Sparse = 2,
NcaStorageBaseStorageType_Indirect = 3,
NcaStorageBaseStorageType_Compressed = 4,
NcaStorageBaseStorageType_Count = 5 ///< Total values supported by this enum.
} NcaStorageBaseStorageType;
/// Used to perform multi-layered reads within a single NCA FS section.
typedef struct {
u8 base_storage_type; ///< NcaStorageBaseStorageType.
NcaFsSectionContext *nca_fs_ctx; ///< NCA FS section context used to initialize this context.
BucketTreeContext *sparse_storage; ///< Sparse storage context.
BucketTreeContext *aes_ctr_ex_storage; ///< AesCtrEx storage context.
BucketTreeContext *indirect_storage; ///< Indirect storage context.
BucketTreeContext *compressed_storage; ///< Compressed storage context.
} NcaStorageContext;
/// Initializes a NCA storage context using a NCA FS section context, optionally providing a pointer to a base NcaStorageContext.
/// 'base_ctx' shall be provided if dealing with a patch NCA with available base NCA data. This is needed to perform combined reads between a base NCA and a patch NCA.
/// 'base_ctx' shall be NULL if dealing with a base NCA *or* a patch NCA with missing base NCA data.
bool ncaStorageInitializeContext(NcaStorageContext *out, NcaFsSectionContext *nca_fs_ctx, NcaStorageContext *base_ctx);
/// Retrieves the underlying NCA FS section's hierarchical hash target layer extents. Virtual extents may be returned, depending on the base storage type.
/// Output offset is relative to the start of the NCA FS section.
/// Either 'out_offset' or 'out_size' can be NULL, but at least one of them must be a valid pointer.
bool ncaStorageGetHashTargetExtents(NcaStorageContext *ctx, u64 *out_offset, u64 *out_size);
/// Reads data from the NCA storage using a previously initialized NcaStorageContext.
bool ncaStorageRead(NcaStorageContext *ctx, void *out, u64 read_size, u64 offset);
/// Checks if the provided block extents are within the provided Patch NcaStorageContext's Indirect Storage.
bool ncaStorageIsBlockWithinPatchStorageRange(NcaStorageContext *ctx, u64 offset, u64 size, bool *out);
/// Frees a previously initialized NCA storage context.
void ncaStorageFreeContext(NcaStorageContext *ctx);
/// Helper inline functions.
NX_INLINE bool ncaStorageIsValidContext(NcaStorageContext *ctx)
{
return (ctx && ctx->base_storage_type >= NcaStorageBaseStorageType_Regular && ctx->base_storage_type <= NcaStorageBaseStorageType_Compressed && ctx->nca_fs_ctx && \
ctx->nca_fs_ctx->enabled);
}
#ifdef __cplusplus
}
#endif
#endif /* __NCA_STORAGE_H__ */

732
include/core/npdm.h Normal file
View File

@ -0,0 +1,732 @@
/*
* npdm.h
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __NPDM_H__
#define __NPDM_H__
#include "pfs.h"
#ifdef __cplusplus
extern "C" {
#endif
#define NPDM_META_MAGIC 0x4D455441 /* "META". */
#define NPDM_ACID_MAGIC 0x41434944 /* "ACID". */
#define NPDM_ACI0_MAGIC 0x41434930 /* "ACI0". */
#define NPDM_MAIN_THREAD_MAX_PRIORITY 0x3F
#define NPDM_MAIN_THREAD_MAX_CORE_NUMBER 3
#define NPDM_SYSTEM_RESOURCE_MAX_SIZE 0x1FE00000
#define NPDM_MAIN_THREAD_STACK_SIZE_ALIGNMENT 0x1000
/// 'NpdmSignatureKeyGeneration_Current' will always point to the last known key generation value.
/// TODO: update on signature keygen changes.
typedef enum {
NpdmSignatureKeyGeneration_Since100NUP = 0, ///< 1.0.0 - 8.1.1.
NpdmSignatureKeyGeneration_Since900NUP = 1, ///< 9.0.0+.
NpdmSignatureKeyGeneration_Current = NpdmSignatureKeyGeneration_Since900NUP,
NpdmSignatureKeyGeneration_Max = (NpdmSignatureKeyGeneration_Current + 1)
} NpdmSignatureKeyGeneration;
typedef enum {
NpdmProcessAddressSpace_AddressSpace32Bit = 0,
NpdmProcessAddressSpace_AddressSpace64BitOld = 1,
NpdmProcessAddressSpace_AddressSpace32BitNoReserved = 2,
NpdmProcessAddressSpace_AddressSpace64Bit = 3,
NpdmProcessAddressSpace_Count = 4 ///< Total values supported by this enum.
} NpdmProcessAddressSpace;
typedef struct {
u8 is_64bit_instruction : 1;
u8 process_address_space : 3; ///< NpdmProcessAddressSpace.
u8 optimize_memory_allocation : 1;
u8 disable_device_address_space_merge : 1;
u8 reserved : 2;
} NpdmMetaFlags;
NXDT_ASSERT(NpdmMetaFlags, 0x1);
/// This is the start of every NPDM file.
/// This is followed by ACID and ACI0 sections, both with variable offsets and sizes.
typedef struct {
u32 magic; ///< "NPDM".
u8 acid_signature_key_generation; ///< NpdmSignatureKeyGeneration.
u8 reserved_1[0x7];
NpdmMetaFlags flags;
u8 reserved_2;
u8 main_thread_priority; ///< Must not exceed NPDM_MAIN_THREAD_MAX_PRIORITY.
u8 main_thread_core_number; ///< Must not exceed NPDM_MAIN_THREAD_MAX_CORE_NUMBER.
u8 reserved_3[0x4];
u32 system_resource_size; ///< Must not exceed NPDM_SYSTEM_RESOURCE_MAX_SIZE.
Version version;
u32 main_thread_stack_size; ///< Must be aligned to NPDM_MAIN_THREAD_STACK_SIZE_ALIGNMENT.
char name[0x10]; ///< Usually set to "Application".
char product_code[0x10]; ///< Usually zeroed out.
u8 reserved_4[0x30];
u32 aci_offset; ///< Offset value relative to the start of this header.
u32 aci_size;
u32 acid_offset; ///< Offset value relative to the start of this header.
u32 acid_size;
} NpdmMetaHeader;
NXDT_ASSERT(NpdmMetaHeader, 0x80);
typedef enum {
NpdmMemoryRegion_Application = 0,
NpdmMemoryRegion_Applet = 1,
NpdmMemoryRegion_SecureSystem = 2,
NpdmMemoryRegion_NonSecureSystem = 3,
NpdmMemoryRegion_Count = 4, ///< Total values supported by this enum.
/// Old.
NpdmMemoryRegion_NonSecure = NpdmMemoryRegion_Application,
NpdmMemoryRegion_Secure = NpdmMemoryRegion_Applet
} NpdmMemoryRegion;
typedef struct {
u32 production : 1;
u32 unqualified_approval : 1;
u32 memory_region : 4; ///< NpdmMemoryRegion.
u32 reserved : 26;
} NpdmAcidFlags;
NXDT_ASSERT(NpdmAcidFlags, 0x4);
/// This is the start of an ACID section.
/// This is followed by FsAccessControl, SrvAccessControl and KernelCapability descriptors, each one aligned to a 0x10 byte boundary using zero padding (if needed).
typedef struct {
u8 signature[0x100]; ///< RSA-2048-PSS with SHA-256 signature over the rest of the ACID section, using the value from the 'size' member.
u8 public_key[0x100]; ///< RSA public key used to verify the ACID signature from the Program NCA header.
u32 magic; ///< "ACID".
u32 size; ///< Must be equal to ACID section size from the META header minus 0x100 (ACID signature size).
u8 version; ///< 9.0.0+.
u8 unknown; ///< 14.0.0+.
u8 reserved_1[0x2];
NpdmAcidFlags flags;
u64 program_id_min;
u64 program_id_max;
u32 fs_access_control_offset; ///< Offset value relative to the start of this header.
u32 fs_access_control_size;
u32 srv_access_control_offset; ///< Offset value relative to the start of this header.
u32 srv_access_control_size;
u32 kernel_capability_offset; ///< Offset value relative to the start of this header.
u32 kernel_capability_size;
u8 reserved_2[0x8];
} NpdmAcidHeader;
NXDT_ASSERT(NpdmAcidHeader, 0x240);
/// This is the start of an ACI0 section.
/// This is followed by a FsAccessControl data block, as well as SrvAccessControl and KernelCapability descriptors, each one aligned to a 0x10 byte boundary using zero padding (if needed).
typedef struct {
u32 magic;
u8 reserved_1[0xC];
u64 program_id;
u8 reserved_2[0x8];
u32 fs_access_control_offset; ///< Offset value relative to the start of this header.
u32 fs_access_control_size;
u32 srv_access_control_offset; ///< Offset value relative to the start of this header.
u32 srv_access_control_size;
u32 kernel_capability_offset; ///< Offset value relative to the start of this header.
u32 kernel_capability_size;
u8 reserved_3[0x8];
} NpdmAciHeader;
NXDT_ASSERT(NpdmAciHeader, 0x40);
typedef enum {
NpdmFsAccessControlFlags_None = 0,
NpdmFsAccessControlFlags_ApplicationInfo = BITL(0),
NpdmFsAccessControlFlags_BootModeControl = BITL(1),
NpdmFsAccessControlFlags_Calibration = BITL(2),
NpdmFsAccessControlFlags_SystemSaveData = BITL(3),
NpdmFsAccessControlFlags_GameCard = BITL(4),
NpdmFsAccessControlFlags_SaveDataBackUp = BITL(5),
NpdmFsAccessControlFlags_SaveDataManagement = BITL(6),
NpdmFsAccessControlFlags_BisAllRaw = BITL(7),
NpdmFsAccessControlFlags_GameCardRaw = BITL(8),
NpdmFsAccessControlFlags_GameCardPrivate = BITL(9),
NpdmFsAccessControlFlags_SetTime = BITL(10),
NpdmFsAccessControlFlags_ContentManager = BITL(11),
NpdmFsAccessControlFlags_ImageManager = BITL(12),
NpdmFsAccessControlFlags_CreateSaveData = BITL(13),
NpdmFsAccessControlFlags_SystemSaveDataManagement = BITL(14),
NpdmFsAccessControlFlags_BisFileSystem = BITL(15),
NpdmFsAccessControlFlags_SystemUpdate = BITL(16),
NpdmFsAccessControlFlags_SaveDataMeta = BITL(17),
NpdmFsAccessControlFlags_DeviceSaveData = BITL(18),
NpdmFsAccessControlFlags_SettingsControl = BITL(19),
NpdmFsAccessControlFlags_SystemData = BITL(20),
NpdmFsAccessControlFlags_SdCard = BITL(21),
NpdmFsAccessControlFlags_Host = BITL(22),
NpdmFsAccessControlFlags_FillBis = BITL(23),
NpdmFsAccessControlFlags_CorruptSaveData = BITL(24),
NpdmFsAccessControlFlags_SaveDataForDebug = BITL(25),
NpdmFsAccessControlFlags_FormatSdCard = BITL(26),
NpdmFsAccessControlFlags_GetRightsId = BITL(27),
NpdmFsAccessControlFlags_RegisterExternalKey = BITL(28),
NpdmFsAccessControlFlags_RegisterUpdatePartition = BITL(29),
NpdmFsAccessControlFlags_SaveDataTransfer = BITL(30),
NpdmFsAccessControlFlags_DeviceDetection = BITL(31),
NpdmFsAccessControlFlags_AccessFailureResolution = BITL(32),
NpdmFsAccessControlFlags_SaveDataTransferVersion2 = BITL(33),
NpdmFsAccessControlFlags_RegisterProgramIndexMapInfo = BITL(34),
NpdmFsAccessControlFlags_CreateOwnSaveData = BITL(35),
NpdmFsAccessControlFlags_MoveCacheStorage = BITL(36),
NpdmFsAccessControlFlags_DeviceTreeBlob = BITL(37),
NpdmFsAccessControlFlags_NotifyErrorContextServiceReady = BITL(38),
NpdmFsAccessControlFlags_Debug = BITL(62),
NpdmFsAccessControlFlags_FullPermission = BITL(63),
NpdmFsAccessControlFlags_Count = 64 ///< Total values supported by this enum.
} NpdmFsAccessControlFlags;
/// FsAccessControl descriptor. Part of the ACID section body.
/// This is followed by:
/// * 'content_owner_id_count' content owner IDs.
/// * 'save_data_owner_id_count' save data owner IDs.
#pragma pack(push, 1)
typedef struct {
u8 version; ///< Always non-zero. Usually set to 1.
u8 content_owner_id_count;
u8 save_data_owner_id_count;
u8 reserved;
u64 flags; ///< NpdmFsAccessControlFlags.
u64 content_owner_id_min;
u64 content_owner_id_max;
u64 save_data_owner_id_min;
u64 save_data_owner_id_max;
} NpdmFsAccessControlDescriptor;
#pragma pack(pop)
NXDT_ASSERT(NpdmFsAccessControlDescriptor, 0x2C);
/// FsAccessControl data. Part of the ACI0 section body.
/// This is followed by:
/// * A NpdmFsAccessControlDataContentOwnerBlock if 'content_owner_info_size' is greater than zero.
/// * A NpdmFsAccessControlDataSaveDataOwnerBlock if 'save_data_owner_info_size' is greater than zero.
/// * If available, this block is padded to a 0x4-byte boundary and followed by 'save_data_owner_id_count' save data owner IDs.
#pragma pack(push, 1)
typedef struct {
u8 version;
u8 reserved_1[0x3];
u64 flags; ///< NpdmFsAccessControlFlags.
u32 content_owner_info_offset; ///< Relative to the start of this block. Only valid if 'content_owner_info_size' is greater than 0.
u32 content_owner_info_size;
u32 save_data_owner_info_offset; ///< Relative to the start of this block. Only valid if 'save_data_owner_info_size' is greater than 0.
u32 save_data_owner_info_size;
} NpdmFsAccessControlData;
#pragma pack(pop)
NXDT_ASSERT(NpdmFsAccessControlData, 0x1C);
/// Placed after NpdmFsAccessControlData if its 'content_owner_info_size' member is greater than zero.
#pragma pack(push, 1)
typedef struct {
u32 content_owner_id_count;
u64 content_owner_id[]; ///< 'content_owner_id_count' content owner IDs.
} NpdmFsAccessControlDataContentOwnerBlock;
#pragma pack(pop)
NXDT_ASSERT(NpdmFsAccessControlDataContentOwnerBlock, 0x4);
typedef enum {
NpdmAccessibility_None = 0,
NpdmAccessibility_Read = BIT(0),
NpdmAccessibility_Write = BIT(1),
NpdmAccessibility_ReadWrite = (NpdmAccessibility_Read | NpdmAccessibility_Write),
NpdmAccessibility_Count = 3 ///< Total values supported by this enum.
} NpdmAccessibility;
/// Placed after NpdmFsAccessControlData / NpdmFsAccessControlDataContentOwnerBlock if the 'save_data_owner_info_size' member from NpdmFsAccessControlData is greater than zero.
/// If available, this block is padded to a 0x4-byte boundary and followed by 'save_data_owner_id_count' save data owner IDs.
typedef struct {
u32 save_data_owner_id_count;
u8 accessibility[]; ///< 'save_data_owner_id_count' NpdmAccessibility fields.
} NpdmFsAccessControlDataSaveDataOwnerBlock;
NXDT_ASSERT(NpdmFsAccessControlDataSaveDataOwnerBlock, 0x4);
/// SrvAccessControl descriptor. Part of the ACID and ACI0 section bodies.
/// This descriptor is composed of a variable number of NpdmSrvAccessControlDescriptorEntry elements, each one with a variable size.
/// Since the total number of services isn't stored anywhere, this descriptor must be parsed until its total size is reached.
typedef struct {
u8 name_length : 3; ///< Service name length minus 1.
u8 reserved : 4;
u8 is_server : 1; ///< Indicates if the service is allowed to be registered.
char name[]; ///< Service name, stored without a NULL terminator. Supports the "*" wildcard character.
} NpdmSrvAccessControlDescriptorEntry;
NXDT_ASSERT(NpdmSrvAccessControlDescriptorEntry, 0x1);
typedef enum {
NpdmKernelCapabilityEntryBitmaskSize_ThreadInfo = 3,
NpdmKernelCapabilityEntryBitmaskSize_EnableSystemCalls = 4,
NpdmKernelCapabilityEntryBitmaskSize_MemoryMap = 6,
NpdmKernelCapabilityEntryBitmaskSize_IoMemoryMap = 7,
NpdmKernelCapabilityEntryBitmaskSize_MemoryRegionMap = 10,
NpdmKernelCapabilityEntryBitmaskSize_EnableInterrupts = 11,
NpdmKernelCapabilityEntryBitmaskSize_MiscParams = 13,
NpdmKernelCapabilityEntryBitmaskSize_KernelVersion = 14,
NpdmKernelCapabilityEntryBitmaskSize_HandleTableSize = 15,
NpdmKernelCapabilityEntryBitmaskSize_MiscFlags = 16
} NpdmKernelCapabilityEntryBitmaskSize;
typedef enum {
NpdmKernelCapabilityEntryBitmaskPattern_ThreadInfo = BIT(NpdmKernelCapabilityEntryBitmaskSize_ThreadInfo) - 1,
NpdmKernelCapabilityEntryBitmaskPattern_EnableSystemCalls = BIT(NpdmKernelCapabilityEntryBitmaskSize_EnableSystemCalls) - 1,
NpdmKernelCapabilityEntryBitmaskPattern_MemoryMap = BIT(NpdmKernelCapabilityEntryBitmaskSize_MemoryMap) - 1,
NpdmKernelCapabilityEntryBitmaskPattern_IoMemoryMap = BIT(NpdmKernelCapabilityEntryBitmaskSize_IoMemoryMap) - 1,
NpdmKernelCapabilityEntryBitmaskPattern_MemoryRegionMap = BIT(NpdmKernelCapabilityEntryBitmaskSize_MemoryRegionMap) - 1,
NpdmKernelCapabilityEntryBitmaskPattern_EnableInterrupts = BIT(NpdmKernelCapabilityEntryBitmaskSize_EnableInterrupts) - 1,
NpdmKernelCapabilityEntryBitmaskPattern_MiscParams = BIT(NpdmKernelCapabilityEntryBitmaskSize_MiscParams) - 1,
NpdmKernelCapabilityEntryBitmaskPattern_KernelVersion = BIT(NpdmKernelCapabilityEntryBitmaskSize_KernelVersion) - 1,
NpdmKernelCapabilityEntryBitmaskPattern_HandleTableSize = BIT(NpdmKernelCapabilityEntryBitmaskSize_HandleTableSize) - 1,
NpdmKernelCapabilityEntryBitmaskPattern_MiscFlags = BIT(NpdmKernelCapabilityEntryBitmaskSize_MiscFlags) - 1
} NpdmKernelCapabilityEntryBitmaskPattern;
/// ThreadInfo entry for the KernelCapability descriptor.
typedef struct {
u32 bitmask : NpdmKernelCapabilityEntryBitmaskSize_ThreadInfo; ///< Always set to NpdmKernelCapabilityEntryBitmaskPattern_ThreadInfo.
u32 padding : 1; ///< Always set to zero.
u32 lowest_priority : 6;
u32 highest_priority : 6;
u32 min_core_number : 8;
u32 max_core_number : 8;
} NpdmThreadInfo;
NXDT_ASSERT(NpdmThreadInfo, 0x4);
/// System call table.
typedef enum {
NpdmSystemCallId_None = 0,
///< System calls for index 0.
NpdmSystemCallId_Reserved1 = BIT(0), ///< SVC 0x00.
NpdmSystemCallId_SetHeapSize = BIT(1), ///< SVC 0x01.
NpdmSystemCallId_SetMemoryPermission = BIT(2), ///< SVC 0x02.
NpdmSystemCallId_SetMemoryAttribute = BIT(3), ///< SVC 0x03.
NpdmSystemCallId_MapMemory = BIT(4), ///< SVC 0x04.
NpdmSystemCallId_UnmapMemory = BIT(5), ///< SVC 0x05.
NpdmSystemCallId_QueryMemory = BIT(6), ///< SVC 0x06.
NpdmSystemCallId_ExitProcess = BIT(7), ///< SVC 0x07.
NpdmSystemCallId_CreateThread = BIT(8), ///< SVC 0x08.
NpdmSystemCallId_StartThread = BIT(9), ///< SVC 0x09.
NpdmSystemCallId_ExitThread = BIT(10), ///< SVC 0x0A.
NpdmSystemCallId_SleepThread = BIT(11), ///< SVC 0x0B.
NpdmSystemCallId_GetThreadPriority = BIT(12), ///< SVC 0x0C.
NpdmSystemCallId_SetThreadPriority = BIT(13), ///< SVC 0x0D.
NpdmSystemCallId_GetThreadCoreMask = BIT(14), ///< SVC 0x0E.
NpdmSystemCallId_SetThreadCoreMask = BIT(15), ///< SVC 0x0F.
NpdmSystemCallId_GetCurrentProcessorNumber = BIT(16), ///< SVC 0x10.
NpdmSystemCallId_SignalEvent = BIT(17), ///< SVC 0x11.
NpdmSystemCallId_ClearEvent = BIT(18), ///< SVC 0x12.
NpdmSystemCallId_MapSharedMemory = BIT(19), ///< SVC 0x13.
NpdmSystemCallId_UnmapSharedMemory = BIT(20), ///< SVC 0x14.
NpdmSystemCallId_CreateTransferMemory = BIT(21), ///< SVC 0x15.
NpdmSystemCallId_CloseHandle = BIT(22), ///< SVC 0x16.
NpdmSystemCallId_ResetSignal = BIT(23), ///< SVC 0x17.
///< System calls for index 1.
NpdmSystemCallId_WaitSynchronization = BIT(0), ///< SVC 0x18.
NpdmSystemCallId_CancelSynchronization = BIT(1), ///< SVC 0x19.
NpdmSystemCallId_ArbitrateLock = BIT(2), ///< SVC 0x1A.
NpdmSystemCallId_ArbitrateUnlock = BIT(3), ///< SVC 0x1B.
NpdmSystemCallId_WaitProcessWideKeyAtomic = BIT(4), ///< SVC 0x1C.
NpdmSystemCallId_SignalProcessWideKey = BIT(5), ///< SVC 0x1D.
NpdmSystemCallId_GetSystemTick = BIT(6), ///< SVC 0x1E.
NpdmSystemCallId_ConnectToNamedPort = BIT(7), ///< SVC 0x1F.
NpdmSystemCallId_SendSyncRequestLight = BIT(8), ///< SVC 0x20.
NpdmSystemCallId_SendSyncRequest = BIT(9), ///< SVC 0x21.
NpdmSystemCallId_SendSyncRequestWithUserBuffer = BIT(10), ///< SVC 0x22.
NpdmSystemCallId_SendAsyncRequestWithUserBuffer = BIT(11), ///< SVC 0x23.
NpdmSystemCallId_GetProcessId = BIT(12), ///< SVC 0x24.
NpdmSystemCallId_GetThreadId = BIT(13), ///< SVC 0x25.
NpdmSystemCallId_Break = BIT(14), ///< SVC 0x26.
NpdmSystemCallId_OutputDebugString = BIT(15), ///< SVC 0x27.
NpdmSystemCallId_ReturnFromException = BIT(16), ///< SVC 0x28.
NpdmSystemCallId_GetInfo = BIT(17), ///< SVC 0x29.
NpdmSystemCallId_FlushEntireDataCache = BIT(18), ///< SVC 0x2A.
NpdmSystemCallId_FlushDataCache = BIT(19), ///< SVC 0x2B.
NpdmSystemCallId_MapPhysicalMemory = BIT(20), ///< SVC 0x2C (3.0.0+).
NpdmSystemCallId_UnmapPhysicalMemory = BIT(21), ///< SVC 0x2D (3.0.0+).
NpdmSystemCallId_GetDebugFutureThreadInfo = BIT(22), ///< SVC 0x2E (6.0.0+). Old: NpdmSystemCallId_GetFutureThreadInfo (5.0.0 - 5.1.0).
NpdmSystemCallId_GetLastThreadInfo = BIT(23), ///< SVC 0x2F.
///< System calls for index 2.
NpdmSystemCallId_GetResourceLimitLimitValue = BIT(0), ///< SVC 0x30.
NpdmSystemCallId_GetResourceLimitCurrentValue = BIT(1), ///< SVC 0x31.
NpdmSystemCallId_SetThreadActivity = BIT(2), ///< SVC 0x32.
NpdmSystemCallId_GetThreadContext3 = BIT(3), ///< SVC 0x33.
NpdmSystemCallId_WaitForAddress = BIT(4), ///< SVC 0x34 (4.0.0+).
NpdmSystemCallId_SignalToAddress = BIT(5), ///< SVC 0x35 (4.0.0+).
NpdmSystemCallId_SynchronizePreemptionState = BIT(6), ///< SVC 0x36 (8.0.0+).
NpdmSystemCallId_GetResourceLimitPeakValue = BIT(7), ///< SVC 0x37 (11.0.0+).
NpdmSystemCallId_Reserved2 = BIT(8), ///< SVC 0x38.
NpdmSystemCallId_CreateIoPool = BIT(9), ///< SVC 0x39 (13.0.0+).
NpdmSystemCallId_CreateIoRegion = BIT(10), ///< SVC 0x3A (13.0.0+).
NpdmSystemCallId_Reserved3 = BIT(11), ///< SVC 0x3B.
NpdmSystemCallId_KernelDebug = BIT(12), ///< SVC 0x3C (4.0.0+). Old: NpdmSystemCallId_DumpInfo (1.0.0 - 3.0.2).
NpdmSystemCallId_ChangeKernelTraceState = BIT(13), ///< SVC 0x3D (4.0.0+).
NpdmSystemCallId_Reserved4 = BIT(14), ///< SVC 0x3E.
NpdmSystemCallId_Reserved5 = BIT(15), ///< SVC 0x3F.
NpdmSystemCallId_CreateSession = BIT(16), ///< SVC 0x40.
NpdmSystemCallId_AcceptSession = BIT(17), ///< SVC 0x41.
NpdmSystemCallId_ReplyAndReceiveLight = BIT(18), ///< SVC 0x42.
NpdmSystemCallId_ReplyAndReceive = BIT(19), ///< SVC 0x43.
NpdmSystemCallId_ReplyAndReceiveWithUserBuffer = BIT(20), ///< SVC 0x44.
NpdmSystemCallId_CreateEvent = BIT(21), ///< SVC 0x45.
NpdmSystemCallId_MapIoRegion = BIT(22), ///< SVC 0x46 (13.0.0+).
NpdmSystemCallId_UnmapIoRegion = BIT(23), ///< SVC 0x47 (13.0.0+).
///< System calls for index 3.
NpdmSystemCallId_MapPhysicalMemoryUnsafe = BIT(0), ///< SVC 0x48 (5.0.0+).
NpdmSystemCallId_UnmapPhysicalMemoryUnsafe = BIT(1), ///< SVC 0x49 (5.0.0+).
NpdmSystemCallId_SetUnsafeLimit = BIT(2), ///< SVC 0x4A (5.0.0+).
NpdmSystemCallId_CreateCodeMemory = BIT(3), ///< SVC 0x4B (4.0.0+).
NpdmSystemCallId_ControlCodeMemory = BIT(4), ///< SVC 0x4C (4.0.0+).
NpdmSystemCallId_SleepSystem = BIT(5), ///< SVC 0x4D.
NpdmSystemCallId_ReadWriteRegister = BIT(6), ///< SVC 0x4E.
NpdmSystemCallId_SetProcessActivity = BIT(7), ///< SVC 0x4F.
NpdmSystemCallId_CreateSharedMemory = BIT(8), ///< SVC 0x50.
NpdmSystemCallId_MapTransferMemory = BIT(9), ///< SVC 0x51.
NpdmSystemCallId_UnmapTransferMemory = BIT(10), ///< SVC 0x52.
NpdmSystemCallId_CreateInterruptEvent = BIT(11), ///< SVC 0x53.
NpdmSystemCallId_QueryPhysicalAddress = BIT(12), ///< SVC 0x54.
NpdmSystemCallId_QueryIoMapping = BIT(13), ///< SVC 0x55.
NpdmSystemCallId_CreateDeviceAddressSpace = BIT(14), ///< SVC 0x56.
NpdmSystemCallId_AttachDeviceAddressSpace = BIT(15), ///< SVC 0x57.
NpdmSystemCallId_DetachDeviceAddressSpace = BIT(16), ///< SVC 0x58.
NpdmSystemCallId_MapDeviceAddressSpaceByForce = BIT(17), ///< SVC 0x59.
NpdmSystemCallId_MapDeviceAddressSpaceAligned = BIT(18), ///< SVC 0x5A.
NpdmSystemCallId_MapDeviceAddressSpace = BIT(19), ///< SVC 0x5B (1.0.0 - 12.1.0).
NpdmSystemCallId_UnmapDeviceAddressSpace = BIT(20), ///< SVC 0x5C.
NpdmSystemCallId_InvalidateProcessDataCache = BIT(21), ///< SVC 0x5D.
NpdmSystemCallId_StoreProcessDataCache = BIT(22), ///< SVC 0x5E.
NpdmSystemCallId_FlushProcessDataCache = BIT(23), ///< SVC 0x5F.
///< System calls for index 4.
NpdmSystemCallId_DebugActiveProcess = BIT(0), ///< SVC 0x60.
NpdmSystemCallId_BreakDebugProcess = BIT(1), ///< SVC 0x61.
NpdmSystemCallId_TerminateDebugProcess = BIT(2), ///< SVC 0x62.
NpdmSystemCallId_GetDebugEvent = BIT(3), ///< SVC 0x63.
NpdmSystemCallId_ContinueDebugEvent = BIT(4), ///< SVC 0x64.
NpdmSystemCallId_GetProcessList = BIT(5), ///< SVC 0x65.
NpdmSystemCallId_GetThreadList = BIT(6), ///< SVC 0x66.
NpdmSystemCallId_GetDebugThreadContext = BIT(7), ///< SVC 0x67.
NpdmSystemCallId_SetDebugThreadContext = BIT(8), ///< SVC 0x68.
NpdmSystemCallId_QueryDebugProcessMemory = BIT(9), ///< SVC 0x69.
NpdmSystemCallId_ReadDebugProcessMemory = BIT(10), ///< SVC 0x6A.
NpdmSystemCallId_WriteDebugProcessMemory = BIT(11), ///< SVC 0x6B.
NpdmSystemCallId_SetHardwareBreakPoint = BIT(12), ///< SVC 0x6C.
NpdmSystemCallId_GetDebugThreadParam = BIT(13), ///< SVC 0x6D.
NpdmSystemCallId_Reserved6 = BIT(14), ///< SVC 0x6E.
NpdmSystemCallId_GetSystemInfo = BIT(15), ///< SVC 0x6F (5.0.0+).
NpdmSystemCallId_CreatePort = BIT(16), ///< SVC 0x70.
NpdmSystemCallId_ManageNamedPort = BIT(17), ///< SVC 0x71.
NpdmSystemCallId_ConnectToPort = BIT(18), ///< SVC 0x72.
NpdmSystemCallId_SetProcessMemoryPermission = BIT(19), ///< SVC 0x73.
NpdmSystemCallId_MapProcessMemory = BIT(20), ///< SVC 0x74.
NpdmSystemCallId_UnmapProcessMemory = BIT(21), ///< SVC 0x75.
NpdmSystemCallId_QueryProcessMemory = BIT(22), ///< SVC 0x76.
NpdmSystemCallId_MapProcessCodeMemory = BIT(23), ///< SVC 0x77.
///< System calls for index 5.
NpdmSystemCallId_UnmapProcessCodeMemory = BIT(0), ///< SVC 0x78.
NpdmSystemCallId_CreateProcess = BIT(1), ///< SVC 0x79.
NpdmSystemCallId_StartProcess = BIT(2), ///< SVC 0x7A.
NpdmSystemCallId_TerminateProcess = BIT(3), ///< SVC 0x7B.
NpdmSystemCallId_GetProcessInfo = BIT(4), ///< SVC 0x7C.
NpdmSystemCallId_CreateResourceLimit = BIT(5), ///< SVC 0x7D.
NpdmSystemCallId_SetResourceLimitLimitValue = BIT(6), ///< SVC 0x7E.
NpdmSystemCallId_CallSecureMonitor = BIT(7), ///< SVC 0x7F.
NpdmSystemCallId_Reserved7 = BIT(8), ///< SVC 0x80.
NpdmSystemCallId_Reserved8 = BIT(9), ///< SVC 0x81.
NpdmSystemCallId_Reserved9 = BIT(10), ///< SVC 0x82.
NpdmSystemCallId_Reserved10 = BIT(11), ///< SVC 0x83.
NpdmSystemCallId_Reserved11 = BIT(12), ///< SVC 0x84.
NpdmSystemCallId_Reserved12 = BIT(13), ///< SVC 0x85.
NpdmSystemCallId_Reserved13 = BIT(14), ///< SVC 0x86.
NpdmSystemCallId_Reserved14 = BIT(15), ///< SVC 0x87.
NpdmSystemCallId_Reserved15 = BIT(16), ///< SVC 0x88.
NpdmSystemCallId_Reserved16 = BIT(17), ///< SVC 0x89.
NpdmSystemCallId_Reserved17 = BIT(18), ///< SVC 0x8A.
NpdmSystemCallId_Reserved18 = BIT(19), ///< SVC 0x8B.
NpdmSystemCallId_Reserved19 = BIT(20), ///< SVC 0x8C.
NpdmSystemCallId_Reserved20 = BIT(21), ///< SVC 0x8D.
NpdmSystemCallId_Reserved21 = BIT(22), ///< SVC 0x8E.
NpdmSystemCallId_Reserved22 = BIT(23), ///< SVC 0x8F.
///< System calls for index 6.
NpdmSystemCallId_MapInsecureMemory = BIT(0), ///< SVC 0x90 (15.0.0+).
NpdmSystemCallId_UnmapInsecureMemory = BIT(1), ///< SVC 0x91 (15.0.0+).
NpdmSystemCallId_Reserved23 = BIT(2), ///< SVC 0x92.
NpdmSystemCallId_Reserved24 = BIT(3), ///< SVC 0x93.
NpdmSystemCallId_Reserved25 = BIT(4), ///< SVC 0x94.
NpdmSystemCallId_Reserved26 = BIT(5), ///< SVC 0x95.
NpdmSystemCallId_Reserved27 = BIT(6), ///< SVC 0x96.
NpdmSystemCallId_Reserved28 = BIT(7), ///< SVC 0x97.
NpdmSystemCallId_Reserved29 = BIT(8), ///< SVC 0x98.
NpdmSystemCallId_Reserved30 = BIT(9), ///< SVC 0x99.
NpdmSystemCallId_Reserved31 = BIT(10), ///< SVC 0x9A.
NpdmSystemCallId_Reserved32 = BIT(11), ///< SVC 0x9B.
NpdmSystemCallId_Reserved33 = BIT(12), ///< SVC 0x9C.
NpdmSystemCallId_Reserved34 = BIT(13), ///< SVC 0x9D.
NpdmSystemCallId_Reserved35 = BIT(14), ///< SVC 0x9E.
NpdmSystemCallId_Reserved36 = BIT(15), ///< SVC 0x9F.
NpdmSystemCallId_Reserved37 = BIT(16), ///< SVC 0xA0.
NpdmSystemCallId_Reserved38 = BIT(17), ///< SVC 0xA1.
NpdmSystemCallId_Reserved39 = BIT(18), ///< SVC 0xA2.
NpdmSystemCallId_Reserved40 = BIT(19), ///< SVC 0xA3.
NpdmSystemCallId_Reserved41 = BIT(20), ///< SVC 0xA4.
NpdmSystemCallId_Reserved42 = BIT(21), ///< SVC 0xA5.
NpdmSystemCallId_Reserved43 = BIT(22), ///< SVC 0xA6.
NpdmSystemCallId_Reserved44 = BIT(23), ///< SVC 0xA7.
///< System calls for index 7.
NpdmSystemCallId_Reserved45 = BIT(0), ///< SVC 0xA8.
NpdmSystemCallId_Reserved46 = BIT(1), ///< SVC 0xA9.
NpdmSystemCallId_Reserved47 = BIT(2), ///< SVC 0xAA.
NpdmSystemCallId_Reserved48 = BIT(3), ///< SVC 0xAB.
NpdmSystemCallId_Reserved49 = BIT(4), ///< SVC 0xAC.
NpdmSystemCallId_Reserved50 = BIT(5), ///< SVC 0xAD.
NpdmSystemCallId_Reserved51 = BIT(6), ///< SVC 0xAE.
NpdmSystemCallId_Reserved52 = BIT(7), ///< SVC 0xAF.
NpdmSystemCallId_Reserved53 = BIT(8), ///< SVC 0xB0.
NpdmSystemCallId_Reserved54 = BIT(9), ///< SVC 0xB1.
NpdmSystemCallId_Reserved55 = BIT(10), ///< SVC 0xB2.
NpdmSystemCallId_Reserved56 = BIT(11), ///< SVC 0xB3.
NpdmSystemCallId_Reserved57 = BIT(12), ///< SVC 0xB4.
NpdmSystemCallId_Reserved58 = BIT(13), ///< SVC 0xB5.
NpdmSystemCallId_Reserved59 = BIT(14), ///< SVC 0xB6.
NpdmSystemCallId_Reserved60 = BIT(15), ///< SVC 0xB7.
NpdmSystemCallId_Reserved61 = BIT(16), ///< SVC 0xB8.
NpdmSystemCallId_Reserved62 = BIT(17), ///< SVC 0xB9.
NpdmSystemCallId_Reserved63 = BIT(18), ///< SVC 0xBA.
NpdmSystemCallId_Reserved64 = BIT(19), ///< SVC 0xBB.
NpdmSystemCallId_Reserved65 = BIT(20), ///< SVC 0xBC.
NpdmSystemCallId_Reserved66 = BIT(21), ///< SVC 0xBD.
NpdmSystemCallId_Reserved67 = BIT(22), ///< SVC 0xBE.
NpdmSystemCallId_Reserved68 = BIT(23), ///< SVC 0xBF.
NpdmSystemCallId_Count = 0xC0 ///< Total values supported by this enum.
} NpdmSystemCallId;
/// EnableSystemCalls entry for the KernelCapability descriptor.
typedef struct {
u32 bitmask : NpdmKernelCapabilityEntryBitmaskSize_EnableSystemCalls; ///< Always set to NpdmKernelCapabilityEntryBitmaskPattern_EnableSystemCalls.
u32 padding : 1; ///< Always set to zero.
u32 system_call_ids : 24; ///< NpdmSystemCallId.
u32 index : 3; ///< System calls index.
} NpdmEnableSystemCalls;
NXDT_ASSERT(NpdmEnableSystemCalls, 0x4);
typedef enum {
NpdmPermissionType_RW = 0,
NpdmPermissionType_RO = 1,
NpdmPermissionType_Count = 2 ///< Total values supported by this enum.
} NpdmPermissionType;
typedef enum {
NpdmMappingType_Io = 0,
NpdmMappingType_Static = 1,
NpdmMappingType_Count = 2 ///< Total values supported by this enum.
} NpdmMappingType;
typedef struct {
u32 bitmask : NpdmKernelCapabilityEntryBitmaskSize_MemoryMap; ///< Always set to NpdmKernelCapabilityEntryBitmaskPattern_MemoryMap.
u32 padding : 1; ///< Always set to zero.
u32 begin_address : 24; ///< begin_address << 12.
u32 permission_type : 1; ///< NpdmPermissionType.
} NpdmMemoryMapType1;
NXDT_ASSERT(NpdmMemoryMapType1, 0x4);
typedef struct {
u32 bitmask : NpdmKernelCapabilityEntryBitmaskSize_MemoryMap; ///< Always set to NpdmKernelCapabilityEntryBitmaskPattern_MemoryMap.
u32 padding : 1; ///< Always set to zero.
u32 size : 20; ///< size << 12.
u32 reserved : 4;
u32 mapping_type : 1; ///< NpdmMappingType.
} NpdmMemoryMapType2;
NXDT_ASSERT(NpdmMemoryMapType2, 0x4);
/// MemoryMap entry for the KernelCapability descriptor.
/// These are always stored in pairs of MemoryMapType1 + MemoryMapType2 entries.
typedef struct {
union {
NpdmMemoryMapType1 type1;
NpdmMemoryMapType2 type2;
};
} NpdmMemoryMap;
NXDT_ASSERT(NpdmMemoryMap, 0x4);
/// IoMemoryMap entry for the KernelCapability descriptor.
typedef struct {
u32 bitmask : NpdmKernelCapabilityEntryBitmaskSize_IoMemoryMap; ///< Always set to NpdmKernelCapabilityEntryBitmaskPattern_IoMemoryMap.
u32 padding : 1; ///< Always set to zero.
u32 begin_address : 24; ///< begin_address << 12.
} NpdmIoMemoryMap;
NXDT_ASSERT(NpdmIoMemoryMap, 0x4);
typedef enum {
NpdmRegionType_NoMapping = 0,
NpdmRegionType_KernelTraceBuffer = 1,
NpdmRegionType_OnMemoryBootImage = 2,
NpdmRegionType_DTB = 3,
NpdmRegionType_Count = 4 ///< Total values supported by this enum.
} NpdmRegionType;
/// MemoryRegionMap entry for the KernelCapability descriptor.
typedef struct {
u32 bitmask : NpdmKernelCapabilityEntryBitmaskSize_MemoryRegionMap; ///< Always set to NpdmKernelCapabilityEntryBitmaskPattern_MemoryRegionMap.
u32 padding : 1; ///< Always set to zero.
u32 region_type_0 : 6; ///< NpdmRegionType.
u32 permission_type_0 : 1; ///< NpdmPermissionType.
u32 region_type_1 : 6; ///< NpdmRegionType.
u32 permission_type_1 : 1; ///< NpdmPermissionType.
u32 region_type_2 : 6; ///< NpdmRegionType.
u32 permission_type_2 : 1; ///< NpdmPermissionType.
} NpdmMemoryRegionMap;
NXDT_ASSERT(NpdmMemoryRegionMap, 0x4);
/// EnableInterrupts entry for the KernelCapability descriptor.
typedef struct {
u32 bitmask : NpdmKernelCapabilityEntryBitmaskSize_EnableInterrupts; ///< Always set to NpdmKernelCapabilityEntryBitmaskPattern_EnableInterrupts.
u32 padding : 1; ///< Always set to zero.
u32 interrupt_number_0 : 10; ///< 0x3FF means empty.
u32 interrupt_number_1 : 10; ///< 0x3FF means empty.
} NpdmEnableInterrupts;
NXDT_ASSERT(NpdmEnableInterrupts, 0x4);
typedef enum {
NpdmProgramType_System = 0,
NpdmProgramType_Application = 1,
NpdmProgramType_Applet = 2,
NpdmProgramType_Count = 3 ///< Total values supported by this enum.
} NpdmProgramType;
/// MiscParams entry for the KernelCapability descriptor.
/// Defaults to 0 if this entry doesn't exist.
typedef struct {
u32 bitmask : NpdmKernelCapabilityEntryBitmaskSize_MiscParams; ///< Always set to NpdmKernelCapabilityEntryBitmaskPattern_MiscParams.
u32 padding : 1; ///< Always set to zero.
u32 program_type : 3; ///< NpdmProgramType.
u32 reserved : 15;
} NpdmMiscParams;
NXDT_ASSERT(NpdmMiscParams, 0x4);
/// KernelVersion entry for the KernelCapability descriptor.
/// This is derived from/equivalent to SDK version.
typedef struct {
u32 bitmask : NpdmKernelCapabilityEntryBitmaskSize_KernelVersion; ///< Always set to NpdmKernelCapabilityEntryBitmaskPattern_KernelVersion.
u32 padding : 1; ///< Always set to zero.
u32 minor_version : 4; ///< SDK minor version.
u32 major_version : 13; ///< SDK major version + 4.
} NpdmKernelVersion;
NXDT_ASSERT(NpdmKernelVersion, 0x4);
/// HandleTableSize entry for the KernelCapability descriptor.
typedef struct {
u32 bitmask : NpdmKernelCapabilityEntryBitmaskSize_HandleTableSize; ///< Always set to NpdmKernelCapabilityEntryBitmaskPattern_HandleTableSize.
u32 padding : 1; ///< Always set to zero.
u32 handle_table_size : 10;
u32 reserved : 6;
} NpdmHandleTableSize;
NXDT_ASSERT(NpdmHandleTableSize, 0x4);
/// MiscFlags entry for the KernelCapability descriptor.
typedef struct {
u32 bitmask : NpdmKernelCapabilityEntryBitmaskSize_MiscFlags; ///< Always set to NpdmKernelCapabilityEntryBitmaskPattern_MiscFlags.
u32 padding : 1; ///< Always set to zero.
u32 enable_debug : 1;
u32 force_debug : 1;
u32 reserved : 13;
} NpdmMiscFlags;
NXDT_ASSERT(NpdmMiscFlags, 0x4);
/// KernelCapability descriptor. Part of the ACID and ACI0 section bodies.
/// This descriptor is composed of a variable number of u32 entries. Thus, the entry count can be calculated by dividing the KernelCapability descriptor size by 4.
/// The entry type is identified by a pattern of "01...11" (zero followed by ones) in the low u16, counting from the LSB. The variable number of ones must never exceed 16 (entirety of the low u16).
typedef struct {
u32 value;
} NpdmKernelCapabilityDescriptorEntry;
NXDT_ASSERT(NpdmKernelCapabilityDescriptorEntry, 0x4);
typedef struct {
u8 *raw_data; ///< Pointer to a dynamically allocated buffer that holds the raw NPDM.
u64 raw_data_size; ///< Raw NPDM size.
NpdmMetaHeader *meta_header; ///< Pointer to the NpdmMetaHeader within 'raw_data'.
NpdmAcidHeader *acid_header; ///< Pointer to the NpdmAcidHeader within 'raw_data'.
NpdmFsAccessControlDescriptor *acid_fac_descriptor; ///< Pointer to the NpdmFsAccessControlDescriptor within the NPDM ACID section.
NpdmSrvAccessControlDescriptorEntry *acid_sac_descriptor; ///< Pointer to the first NpdmSrvAccessControlDescriptorEntry within the NPDM ACID section, if available.
NpdmKernelCapabilityDescriptorEntry *acid_kc_descriptor; ///< Pointer to the first NpdmKernelCapabilityDescriptorEntry within the NPDM ACID section, if available.
NpdmAciHeader *aci_header; ///< Pointer to the NpdmAciHeader within 'raw_data'.
NpdmFsAccessControlData *aci_fac_data; ///< Pointer to the NpdmFsAccessControlData within the NPDM ACI0 section.
NpdmSrvAccessControlDescriptorEntry *aci_sac_descriptor; ///< Pointer to the first NpdmSrvAccessControlDescriptorEntry within the NPDM ACI0 section, if available.
NpdmKernelCapabilityDescriptorEntry *aci_kc_descriptor; ///< Pointer to the first NpdmKernelCapabilityDescriptorEntry within the NPDM ACI0 section, if available.
} NpdmContext;
/// Initializes a NpdmContext using a previously initialized PartitionFileSystemContext (which must belong to the ExeFS from a Program NCA).
bool npdmInitializeContext(NpdmContext *out, PartitionFileSystemContext *pfs_ctx);
/// Helper inline functions.
NX_INLINE void npdmFreeContext(NpdmContext *npdm_ctx)
{
if (!npdm_ctx) return;
if (npdm_ctx->raw_data) free(npdm_ctx->raw_data);
memset(npdm_ctx, 0, sizeof(NpdmContext));
}
NX_INLINE bool npdmIsValidContext(NpdmContext *npdm_ctx)
{
return (npdm_ctx && npdm_ctx->raw_data && npdm_ctx->raw_data_size && npdm_ctx->meta_header && npdm_ctx->acid_header && npdm_ctx->acid_fac_descriptor && \
((npdm_ctx->acid_header->srv_access_control_size && npdm_ctx->acid_sac_descriptor) || (!npdm_ctx->acid_header->srv_access_control_size && !npdm_ctx->acid_sac_descriptor)) && \
((npdm_ctx->acid_header->kernel_capability_size && npdm_ctx->acid_kc_descriptor) || (!npdm_ctx->acid_header->kernel_capability_size && !npdm_ctx->acid_kc_descriptor)) && \
npdm_ctx->aci_header && npdm_ctx->aci_fac_data && \
((npdm_ctx->aci_header->srv_access_control_size && npdm_ctx->aci_sac_descriptor) || (!npdm_ctx->aci_header->srv_access_control_size && !npdm_ctx->aci_sac_descriptor)) && \
((npdm_ctx->aci_header->kernel_capability_size && npdm_ctx->aci_kc_descriptor) || (!npdm_ctx->aci_header->kernel_capability_size && !npdm_ctx->aci_kc_descriptor)));
}
/// Returns a value that can be loooked up in the NpdmKernelCapabilityEntryBitmaskPattern enum.
NX_INLINE u32 npdmGetKernelCapabilityDescriptorEntryBitmaskPattern(NpdmKernelCapabilityDescriptorEntry *entry)
{
return (entry ? (((entry->value + 1) & ~entry->value) - 1) : 0);
}
#ifdef __cplusplus
}
#endif
#endif /* __NPDM_H__ */

169
include/core/nso.h Normal file
View File

@ -0,0 +1,169 @@
/*
* nso.h
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __NSO_H__
#define __NSO_H__
#include "pfs.h"
#ifdef __cplusplus
extern "C" {
#endif
#define NSO_HEADER_MAGIC 0x4E534F30 /* "NSO0". */
#define NSO_MOD_MAGIC 0x4D4F4430 /* "MOD0". */
typedef enum {
NsoFlags_None = 0,
NsoFlags_TextCompress = BIT(0), ///< Determines if .text segment is LZ4-compressed.
NsoFlags_RoCompress = BIT(1), ///< Determines if .rodata segment is LZ4-compressed.
NsoFlags_DataCompress = BIT(2), ///< Determines if .data segment is LZ4-compressed.
NsoFlags_TextHash = BIT(3), ///< Determines if .text segment hash must be checked during load.
NsoFlags_RoHash = BIT(4), ///< Determines if .rodata segment hash must be checked during load.
NsoFlags_DataHash = BIT(5), ///< Determines if .data segment hash must be checked during load.
NsoFlags_Count = 6 ///< Total values supported by this enum.
} NsoFlags;
typedef struct {
u32 file_offset; ///< NSO segment offset.
u32 memory_offset; ///< Memory segment offset.
u32 size; ///< Decompressed segment size.
} NsoSegmentInfo;
NXDT_ASSERT(NsoSegmentInfo, 0xC);
typedef struct {
u32 offset; ///< Relative to the .rodata segment start.
u32 size;
} NsoSectionInfo;
NXDT_ASSERT(NsoSectionInfo, 0x8);
/// This is the start of every NSO.
/// This is always followed by a NsoModuleName block.
typedef struct {
u32 magic; ///< "NSO0".
u32 version; ///< Always set to 0.
u8 reserved_1[0x4];
u32 flags; ///< NsoFlags.
NsoSegmentInfo text_segment_info;
u32 module_name_offset; ///< NsoModuleName block offset.
NsoSegmentInfo rodata_segment_info;
u32 module_name_size; ///< NsoModuleName block size.
NsoSegmentInfo data_segment_info;
u32 bss_size;
u8 module_id[0x20]; ///< Also known as build ID.
u32 text_file_size; ///< .text segment compressed size (if NsoFlags_TextCompress is enabled).
u32 rodata_file_size; ///< .rodata segment compressed size (if NsoFlags_RoCompress is enabled).
u32 data_file_size; ///< .data segment compressed size (if NsoFlags_DataCompress is enabled).
u8 reserved_2[0x1C];
NsoSectionInfo api_info_section_info;
NsoSectionInfo dynstr_section_info;
NsoSectionInfo dynsym_section_info;
u8 text_segment_hash[SHA256_HASH_SIZE]; ///< Decompressed .text segment SHA-256 checksum.
u8 rodata_segment_hash[SHA256_HASH_SIZE]; ///< Decompressed .rodata segment SHA-256 checksum.
u8 data_segment_hash[SHA256_HASH_SIZE]; ///< Decompressed .data segment SHA-256 checksum.
} NsoHeader;
NXDT_ASSERT(NsoHeader, 0x100);
/// Usually placed right after NsoHeader, but its actual offset may vary.
/// If the 'module_name_size' member from NsoHeader is greater than 1 and the 'name_length' element from NsoModuleName is greater than 0, 'name' will hold the module name.
typedef struct {
u8 name_length;
char name[];
} NsoModuleName;
NXDT_ASSERT(NsoModuleName, 0x1);
/// Placed at the very start of the decompressed .text segment.
typedef struct {
u32 entry_point;
u32 mod_offset; ///< NsoModHeader block offset (relative to the start of this header). Almost always set to 0x8 (the size of this struct).
} NsoModStart;
NXDT_ASSERT(NsoModStart, 0x8);
/// This is essentially a replacement for the PT_DYNAMIC program header available in ELF binaries.
/// All offsets are signed 32-bit values relative to the start of this header.
/// This is usually placed at the start of the decompressed .text segment, right after a NsoModStart block.
/// However, in some NSOs, it can instead be placed at the start of the decompressed .rodata segment, right after its NsoModuleInfo block.
/// In these cases, the 'mod_offset' value from the NsoModStart block will point to an offset within the .rodata segment.
typedef struct {
u32 magic; ///< "MOD0".
s32 dynamic_offset;
s32 bss_start_offset;
s32 bss_end_offset;
s32 eh_frame_hdr_start_offset;
s32 eh_frame_hdr_end_offset;
s32 module_object_offset; ///< Typically equal to bss_start_offset.
} NsoModHeader;
NXDT_ASSERT(NsoModHeader, 0x1C);
/// Placed at the start of the decompressed .rodata segment + 0x4.
/// If the 'name_length' element is greater than 0, 'name' will hold the module name.
typedef struct {
u32 name_length;
char name[];
} NsoModuleInfo;
NXDT_ASSERT(NsoModuleInfo, 0x4);
typedef struct {
PartitionFileSystemContext *pfs_ctx; ///< PartitionFileSystemContext for the Program NCA FS section #0, which is where this NSO is stored.
PartitionFileSystemEntry *pfs_entry; ///< PartitionFileSystemEntry for this NSO in the Program NCA FS section #0. Used to read NSO data.
char *nso_filename; ///< Pointer to the NSO filename in the Program NCA FS section #0.
NsoHeader nso_header; ///< NSO header.
char *module_name; ///< Pointer to a dynamically allocated buffer that holds the NSO module name, if available. Otherwise, this is set to NULL.
char *module_info_name; ///< Pointer to a dynamically allocated buffer that holds the .rodata module info module name, if available. Otherwise, this is set to NULL.
char *rodata_api_info_section; ///< Pointer to a dynamically allocated buffer that holds the .rodata API info section data, if available. Otherwise, this is set to NULL.
///< Middleware and GuidelineApi entries are retrieved from this section.
u64 rodata_api_info_section_size; ///< .rodata API info section size, if available. Otherwise, this is set to 0. Kept here for convenience - this is part of 'nso_header'.
char *rodata_dynstr_section; ///< Pointer to a dynamically allocated buffer that holds the .rodata dynamic string section data. UnresolvedApi data is retrieved from this section.
u64 rodata_dynstr_section_size; ///< .rodata dynamic string section size. Kept here for convenience - this is part of 'nso_header'.
u8 *rodata_dynsym_section; ///< Pointer to a dynamically allocated buffer that holds the .rodata dynamic symbol section data. Used to retrieve pointers to symbol strings within dynstr.
u64 rodata_dynsym_section_size; ///< .rodata dynamic symbol section size. Kept here for convenience - this is part of 'nso_header'.
} NsoContext;
/// Initializes a NsoContext using a previously initialized PartitionFileSystemContext (which must belong to the ExeFS from a Program NCA) and a PartitionFileSystemEntry belonging to an underlying NSO.
bool nsoInitializeContext(NsoContext *out, PartitionFileSystemContext *pfs_ctx, PartitionFileSystemEntry *pfs_entry);
/// Helper inline functions.
NX_INLINE void nsoFreeContext(NsoContext *nso_ctx)
{
if (!nso_ctx) return;
if (nso_ctx->module_name) free(nso_ctx->module_name);
if (nso_ctx->module_info_name) free(nso_ctx->module_info_name);
if (nso_ctx->rodata_api_info_section) free(nso_ctx->rodata_api_info_section);
if (nso_ctx->rodata_dynstr_section) free(nso_ctx->rodata_dynstr_section);
if (nso_ctx->rodata_dynsym_section) free(nso_ctx->rodata_dynsym_section);
memset(nso_ctx, 0, sizeof(NsoContext));
}
#ifdef __cplusplus
}
#endif
#endif /* __NSO_H__ */

44
include/core/nxdt_bfsar.h Normal file
View File

@ -0,0 +1,44 @@
/*
* nxdt_bfsar.h
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __NXDT_BFSAR_H__
#define __NXDT_BFSAR_H__
#ifdef __cplusplus
extern "C" {
#endif
/// Initializes the BFSAR interface.
bool bfsarInitialize(void);
/// Closes the BFSAR interface.
void bfsarExit(void);
/// Returns a pointer to the BFSAR file path.
const char *bfsarGetFilePath(void);
#ifdef __cplusplus
}
#endif
#endif /* __NXDT_BFSAR_H__ */

View File

@ -0,0 +1,84 @@
/*
* nxdt_includes.h
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __NXDT_INCLUDES_H__
#define __NXDT_INCLUDES_H__
/* C headers. */
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include <stdarg.h>
#include <malloc.h>
#include <errno.h>
#include <ctype.h>
#include <math.h>
#include <time.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <fcntl.h>
#include <dirent.h>
#include <assert.h>
#include <unistd.h>
#ifndef __cplusplus
#include <stdatomic.h>
#else
#include <atomic>
#define _Atomic(X) std::atomic< X >
#endif
/* libnx header. */
#include <switch.h>
/* Internet operations. */
#include <arpa/inet.h>
/* Global defines. */
#include "../defines.h"
/* File/socket based logger. */
#include "nxdt_log.h"
/* Configuration handler. */
#include "config.h"
/* HTTP requests handler. */
#include "http.h"
/* USB Mass Storage support. */
#include "ums.h"
/* SHA3 checksum calculator. */
#include "sha3.h"
/* LZ4 (dec)compression. */
#define LZ4_STATIC_LINKING_ONLY /* Required by LZ4 to enable in-place decompression. */
#include "lz4.h"
/* Horizon OS version structs. */
#include "hos_version_structs.h"
#endif /* __NXDT_INCLUDES_H__ */

97
include/core/nxdt_json.h Normal file
View File

@ -0,0 +1,97 @@
/*
* nxdt_json.h
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __NXDT_JSON_H__
#define __NXDT_JSON_H__
#include <json-c/json.h>
#ifdef __cplusplus
extern "C" {
#endif
/// Parses a JSON object using the provided string.
/// If 'size' is zero, strlen() is used to retrieve the input string length.
/// json_object_put() must be used to free the returned JSON object.
/// Returns NULL if an error occurs.
struct json_object *jsonParseFromString(const char *str, size_t size);
/// Retrieves a JSON object from another object using a path.
/// Path elements must be separated using forward slashes.
/// If 'out_last_element' is provided, the parent JSON object from the last path element will be returned instead.
/// Furthermore, a string duplication of the last path element will be stored in 'out_last_element', which must be freed by the user.
/// Returns NULL if an error occurs.
struct json_object *jsonGetObjectByPath(const struct json_object *obj, const char *path, char **out_last_element);
/// Logs the last JSON error, if available.
void jsonLogLastError(void);
/// Getters and setters for various data types.
/// Path elements must be separated using forward slashes.
bool jsonGetBoolean(const struct json_object *obj, const char *path);
bool jsonSetBoolean(const struct json_object *obj, const char *path, bool value);
int jsonGetInteger(const struct json_object *obj, const char *path);
bool jsonSetInteger(const struct json_object *obj, const char *path, int value);
const char *jsonGetString(const struct json_object *obj, const char *path);
bool jsonSetString(const struct json_object *obj, const char *path, const char *value);
struct json_object *jsonGetArray(const struct json_object *obj, const char *path);
bool jsonSetArray(const struct json_object *obj, const char *path, struct json_object *value);
/// Helper functions to validate specific JSON object types.
NX_INLINE bool jsonValidateBoolean(const struct json_object *obj)
{
return (obj != NULL && json_object_is_type(obj, json_type_boolean));
}
NX_INLINE bool jsonValidateInteger(const struct json_object *obj, int lower_boundary, int upper_boundary)
{
if (!obj || !json_object_is_type(obj, json_type_int) || lower_boundary > upper_boundary) return false;
int val = json_object_get_int(obj);
return (val >= lower_boundary && val <= upper_boundary);
}
NX_INLINE bool jsonValidateString(const struct json_object *obj)
{
return (obj != NULL && json_object_is_type(obj, json_type_string) && json_object_get_string_len(obj) > 0);
}
NX_INLINE bool jsonValidateArray(const struct json_object *obj)
{
return (obj != NULL && json_object_is_type(obj, json_type_array) && json_object_array_length(obj) > 0);
}
NX_INLINE bool jsonValidateObject(const struct json_object *obj)
{
return (obj != NULL && json_object_is_type(obj, json_type_object) && json_object_object_length(obj) > 0);
}
#ifdef __cplusplus
}
#endif
#endif /* __NXDT_JSON_H__ */

151
include/core/nxdt_log.h Normal file
View File

@ -0,0 +1,151 @@
/*
* nxdt_log.h
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __NXDT_LOG_H__
#define __NXDT_LOG_H__
#ifdef __cplusplus
extern "C" {
#endif
/// Used to control logfile verbosity.
#define LOG_LEVEL_DEBUG 0
#define LOG_LEVEL_INFO 1
#define LOG_LEVEL_WARNING 2
#define LOG_LEVEL_ERROR 3
#define LOG_LEVEL_NONE 4
/// Defines the log level used throughout the application.
/// Log messages with a log value lower than this one won't be compiled into the binary.
/// If a value lower than LOG_LEVEL_DEBUG or equal to/greater than LOG_LEVEL_NONE is used, logfile output will be entirely disabled.
#define LOG_LEVEL LOG_LEVEL_DEBUG /* TODO: change before release (warning?). */
#if (LOG_LEVEL >= LOG_LEVEL_DEBUG) && (LOG_LEVEL < LOG_LEVEL_NONE)
/// Helper macros.
#define LOG_MSG_GENERIC(level, fmt, ...) logWriteFormattedStringToLogFile(level, __FILE__, __LINE__, __PRETTY_FUNCTION__, fmt, ##__VA_ARGS__)
#define LOG_MSG_BUF_GENERIC(dst, dst_size, level, fmt, ...) logWriteFormattedStringToBuffer(dst, dst_size, level, __FILE__, __LINE__, __PRETTY_FUNCTION__, fmt, ##__VA_ARGS__)
#define LOG_DATA_GENERIC(data, data_size, level, fmt, ...) logWriteBinaryDataToLogFile(data, data_size, level, __FILE__, __LINE__, __PRETTY_FUNCTION__, fmt, ##__VA_ARGS__)
#if LOG_LEVEL == LOG_LEVEL_DEBUG
#define LOG_MSG_DEBUG(fmt, ...) LOG_MSG_GENERIC(LOG_LEVEL_DEBUG, fmt, ##__VA_ARGS__)
#define LOG_MSG_BUF_DEBUG(dst, dst_size, fmt, ...) LOG_MSG_BUF_GENERIC(dst, dst_size, LOG_LEVEL_DEBUG, fmt, ##__VA_ARGS__)
#define LOG_DATA_DEBUG(data, data_size, fmt, ...) LOG_DATA_GENERIC(data, data_size, LOG_LEVEL_DEBUG, fmt, ##__VA_ARGS__)
#else
#define LOG_MSG_DEBUG(fmt, ...) do {} while(0)
#define LOG_MSG_BUF_DEBUG(dst, dst_size, fmt, ...) do {} while(0)
#define LOG_DATA_DEBUG(data, data_size, fmt, ...) do {} while(0)
#endif /* LOG_LEVEL == LOG_LEVEL_DEBUG */
#if LOG_LEVEL <= LOG_LEVEL_INFO
#define LOG_MSG_INFO(fmt, ...) LOG_MSG_GENERIC(LOG_LEVEL_INFO, fmt, ##__VA_ARGS__)
#define LOG_MSG_BUF_INFO(dst, dst_size, fmt, ...) LOG_MSG_BUF_GENERIC(dst, dst_size, LOG_LEVEL_INFO, fmt, ##__VA_ARGS__)
#define LOG_DATA_INFO(data, data_size, fmt, ...) LOG_DATA_GENERIC(data, data_size, LOG_LEVEL_INFO, fmt, ##__VA_ARGS__)
#else
#define LOG_MSG_INFO(fmt, ...) do {} while(0)
#define LOG_MSG_BUF_INFO(dst, dst_size, fmt, ...) do {} while(0)
#define LOG_DATA_INFO(data, data_size, fmt, ...) do {} while(0)
#endif /* LOG_LEVEL <= LOG_LEVEL_INFO */
#if LOG_LEVEL <= LOG_LEVEL_WARNING
#define LOG_MSG_WARNING(fmt, ...) LOG_MSG_GENERIC(LOG_LEVEL_WARNING, fmt, ##__VA_ARGS__)
#define LOG_MSG_BUF_WARNING(dst, dst_size, fmt, ...) LOG_MSG_BUF_GENERIC(dst, dst_size, LOG_LEVEL_WARNING, fmt, ##__VA_ARGS__)
#define LOG_DATA_WARNING(data, data_size, fmt, ...) LOG_DATA_GENERIC(data, data_size, LOG_LEVEL_WARNING, fmt, ##__VA_ARGS__)
#else
#define LOG_MSG_WARNING(fmt, ...) do {} while(0)
#define LOG_MSG_BUF_WARNING(dst, dst_size, fmt, ...) do {} while(0)
#define LOG_DATA_WARNING(data, data_size, fmt, ...) do {} while(0)
#endif /* LOG_LEVEL <= LOG_LEVEL_WARNING */
#if LOG_LEVEL <= LOG_LEVEL_ERROR
#define LOG_MSG_ERROR(fmt, ...) LOG_MSG_GENERIC(LOG_LEVEL_ERROR, fmt, ##__VA_ARGS__)
#define LOG_MSG_BUF_ERROR(dst, dst_size, fmt, ...) LOG_MSG_BUF_GENERIC(dst, dst_size, LOG_LEVEL_ERROR, fmt, ##__VA_ARGS__)
#define LOG_DATA_ERROR(data, data_size, fmt, ...) LOG_DATA_GENERIC(data, data_size, LOG_LEVEL_ERROR, fmt, ##__VA_ARGS__)
#else
#define LOG_MSG_ERROR(fmt, ...) do {} while(0)
#define LOG_MSG_BUF_ERROR(dst, dst_size, fmt, ...) do {} while(0)
#define LOG_DATA_ERROR(data, data_size, fmt, ...) do {} while(0)
#endif /* LOG_LEVEL <= LOG_LEVEL_ERROR */
/// Writes the provided string to the logfile.
/// If the logfile hasn't been created and/or opened, this function takes care of it.
void logWriteStringToLogFile(const char *src);
/// Writes a formatted log string to the logfile.
/// If the logfile hasn't been created and/or opened, this function takes care of it.
__attribute__((format(printf, 5, 6))) void logWriteFormattedStringToLogFile(u8 level, const char *file_name, int line, const char *func_name, const char *fmt, ...);
/// Writes a formatted log string to the provided buffer.
/// If the buffer isn't big enough to hold both its current contents and the new formatted string, it will be resized.
__attribute__((format(printf, 7, 8))) void logWriteFormattedStringToBuffer(char **dst, size_t *dst_size, u8 level, const char *file_name, int line, const char *func_name, const char *fmt, ...);
/// Writes a formatted log string + a hex string representation of the provided binary data to the logfile.
/// If the logfile hasn't been created and/or opened, this function takes care of it.
__attribute__((format(printf, 7, 8))) void logWriteBinaryDataToLogFile(const void *data, size_t data_size, u8 level, const char *file_name, int line, const char *func_name, const char *fmt, ...);
/// Forces a flush operation on the logfile.
void logFlushLogFile(void);
/// Write any pending data to the logfile, flushes it and then closes it.
void logCloseLogFile(void);
/// Returns a pointer to a dynamically allocated buffer that holds the last error message string, or NULL if there's none.
/// The allocated buffer must be freed by the caller using free().
char *logGetLastMessage(void);
/// (Un)locks the log mutex. Can be used to block other threads and prevent them from writing data to the logfile.
/// Use with caution.
void logControlMutex(bool lock);
#else /* (LOG_LEVEL >= LOG_LEVEL_DEBUG) && (LOG_LEVEL < LOG_LEVEL_NONE) */
/// Helper macros.
#define LOG_MSG_GENERIC(level, fmt, ...) do {} while(0)
#define LOG_MSG_BUF_GENERIC(dst, dst_size, level, fmt, ...) do {} while(0)
#define LOG_DATA_GENERIC(data, data_size, level, fmt, ...) do {} while(0)
#define LOG_MSG_DEBUG(fmt, ...) do {} while(0)
#define LOG_MSG_BUF_DEBUG(dst, dst_size, fmt, ...) do {} while(0)
#define LOG_DATA_DEBUG(data, data_size, fmt, ...) do {} while(0)
#define LOG_MSG_INFO(fmt, ...) do {} while(0)
#define LOG_MSG_BUF_INFO(dst, dst_size, fmt, ...) do {} while(0)
#define LOG_DATA_INFO(data, data_size, fmt, ...) do {} while(0)
#define LOG_MSG_WARNING(fmt, ...) do {} while(0)
#define LOG_MSG_BUF_WARNING(dst, dst_size, fmt, ...) do {} while(0)
#define LOG_DATA_WARNING(data, data_size, fmt, ...) do {} while(0)
#define LOG_MSG_ERROR(fmt, ...) do {} while(0)
#define LOG_MSG_BUF_ERROR(dst, dst_size, fmt, ...) do {} while(0)
#define LOG_DATA_ERROR(data, data_size, fmt, ...) do {} while(0)
#endif /* (LOG_LEVEL >= LOG_LEVEL_DEBUG) && (LOG_LEVEL < LOG_LEVEL_NONE) */
#ifdef __cplusplus
}
#endif
#endif /* __NXDT_LOG_H__ */

249
include/core/nxdt_utils.h Normal file
View File

@ -0,0 +1,249 @@
/*
* nxdt_utils.h
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __NXDT_UTILS_H__
#define __NXDT_UTILS_H__
/* Included here for convenience. */
#include "nxdt_includes.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Scoped lock macro. */
#define SCOPED_LOCK(mtx) for(UtilsScopedLock ANONYMOUS_VARIABLE(scoped_lock) CLEANUP(utilsUnlockScope) = utilsLockScope(mtx); ANONYMOUS_VARIABLE(scoped_lock).cond; ANONYMOUS_VARIABLE(scoped_lock).cond = 0)
/* Scoped try lock macro. */
#define SCOPED_TRY_LOCK(mtx) for(UtilsScopedLock ANONYMOUS_VARIABLE(scoped_lock) CLEANUP(utilsUnlockScope) = utilsTryLockScope(mtx); ANONYMOUS_VARIABLE(scoped_lock).cond; ANONYMOUS_VARIABLE(scoped_lock).cond = 0)
/// Used by scoped locks.
typedef struct {
Mutex *mtx;
bool lock;
int cond;
} UtilsScopedLock;
/// Used to determine which CFW is the application running under.
typedef enum {
UtilsCustomFirmwareType_Unknown = 0,
UtilsCustomFirmwareType_Atmosphere = 1,
UtilsCustomFirmwareType_SXOS = 2,
UtilsCustomFirmwareType_ReiNX = 3,
UtilsCustomFirmwareType_Count = 4 ///< Total values supported by this enum.
} UtilsCustomFirmwareType;
/// Used to handle parsed data from a GitHub release JSON.
/// All strings are dynamically allocated.
typedef struct {
struct json_object *obj; ///< JSON object. Must be freed using json_object_put().
const char *version; ///< Pointer to the version string, referenced by obj.
const char *commit_hash; ///< Pointer to the commit hash string, referenced by obj.
struct tm date; ///< Release date.
const char *changelog; ///< Pointer to the changelog string, referenced by obj.
const char *download_url; ///< Pointer to the download URL string, referenced by obj.
} UtilsGitHubReleaseJsonData;
/// Resource initialization.
/// Called at program startup.
bool utilsInitializeResources(void);
/// Resource deinitialization.
/// Called at program exit.
void utilsCloseResources(void);
/// Returns a pointer to the application launch path.
const char *utilsGetLaunchPath(void);
/// Returns the nxlink socket descriptor, or -1 if an nxlink connection couldn't be established.
int utilsGetNxLinkFileDescriptor(void);
/// Returns a pointer to the FsFileSystem object for the SD card.
FsFileSystem *utilsGetSdCardFileSystemObject(void);
/// Commits SD card filesystem changes.
/// Must be used after closing a file handle from the SD card.
bool utilsCommitSdCardFileSystemChanges(void);
/// Returns an integer that represents the full Atmosphère release version.
/// Use the HOSVER_* macros to retrieve specific version numbers from it.
u32 utilsGetAtmosphereVersion(void);
/// Returns an integer that represents the global key generation used by Atmosphère.
/// The returned value represents an index, so it doesn't match 1:1 the NcaKeyGeneration enum.
u8 utilsGetAtmosphereKeyGeneration(void);
/// Fills the provided SdkAddOnVersion element with the target firmware set by Atmosphère.
void utilsGetAtmosphereTargetFirmware(SdkAddOnVersion *out);
/// Returns a UtilsCustomFirmwareType value.
u8 utilsGetCustomFirmwareType(void);
/// Returns true if the application is running under a Mariko unit.
bool utilsIsMarikoUnit(void);
/// Returns true if the application is running under a development unit.
bool utilsIsDevelopmentUnit(void);
/// Returns true if the application is running under a unit with the Terra platform flag set.
bool utilsIsTerraUnit(void);
/// Returns true if the application is running under applet mode.
bool utilsIsAppletMode(void);
/// Returns a pointer to the FsStorage object for the eMMC BIS System partition.
FsStorage *utilsGetEmmcBisSystemPartitionStorage(void);
/// Blocks HOME button presses, disables screen dimming and auto sleep and overclocks system CPU/MEM.
/// Must be called before starting long-running processes.
/// If state is set to false, regular system behavior is restored.
void utilsSetLongRunningProcessState(bool state);
/// Thread management functions.
bool utilsCreateThread(Thread *out_thread, ThreadFunc func, void *arg, int cpu_id);
void utilsJoinThread(Thread *thread);
/// Formats a string and appends it to the provided buffer.
/// If the buffer isn't big enough to hold both its current contents and the new formatted string, it will be resized.
__attribute__((format(printf, 3, 4))) bool utilsAppendFormattedStringToBuffer(char **dst, size_t *dst_size, const char *fmt, ...);
/// Replaces illegal filesystem characters in the provided NULL-terminated UTF-8 string with underscores ('_').
/// If 'ascii_only' is set to true, all codepoints outside of the [0x20,0x7F) range will also be replaced with underscores.
/// Replacements are performed on a per-codepoint basis, which means the string size in bytes can be reduced by this function.
/// Furthermore, if multiple, consecutive illegal characters are found, they will all get replaced by a single underscore.
void utilsReplaceIllegalCharacters(char *str, bool ascii_only);
/// Trims whitespace characters from the provided string.
void utilsTrimString(char *str);
/// Generates a NULL-terminated hex string representation of the binary data in 'src' and stores it in 'dst'.
/// If 'uppercase' is true, uppercase characters will be used to generate the hex string. Otherwise, lowercase characters will be used.
void utilsGenerateHexString(char *dst, size_t dst_size, const void *src, size_t src_size, bool uppercase);
/// Parses the hex string in 'src' and stores its binary representation in 'dst'.
/// 'src' must match the regex /^(?:[A-Fa-f0-9]{2})+$/.
/// 'src_size' may be zero, in which case strlen() will be used to determine the length of 'src'. Furthermore, 'src_size' must always be a multiple of 2.
/// 'dst_size' must be at least 'src_size / 2'.
/// Returns false if there's an error validating input arguments.
bool utilsParseHexString(void *dst, size_t dst_size, const char *src, size_t src_size);
/// Formats the provided 'size' value to a human-readable size string and stores it in 'dst'.
void utilsGenerateFormattedSizeString(double size, char *dst, size_t dst_size);
/// Saves the total size and free space available from the filesystem pointed to by the input path (e.g. "sdmc:/") to 'out_total' and 'out_free', respectively.
/// Either 'out_total' or 'out_free' can be NULL, but at least one of them must be a valid pointer.
/// Returns false if there's an error.
bool utilsGetFileSystemStatsByPath(const char *path, u64 *out_total, u64 *out_free);
/// Returns true if a file exists.
bool utilsCheckIfFileExists(const char *path);
/// Deletes a ConcatenationFile located at the input path.
void utilsRemoveConcatenationFile(const char *path);
/// Creates a ConcatenationFile at the input path.
bool utilsCreateConcatenationFile(const char *path);
/// Creates a full directory tree using the provided path.
/// If 'create_last_element' is true, the last element from the provided path will be created as well.
void utilsCreateDirectoryTree(const char *path, bool create_last_element);
/// Calculates the size of a directory by recursively traversing all of its child entries.
/// The provided path must be absolute and it must include the virtual device name it belongs to (e.g. "sdmc:/path/to/dir").
bool utilsGetDirectorySize(const char *path, u64 *out_size);
/// Recursively deletes the directory located at the provided path and all of its contents.
/// The provided path must be absolute and it must include the virtual device name it belongs to (e.g. "sdmc:/path/to/dir").
bool utilsDeleteDirectoryRecursively(const char *path);
/// Returns a pointer to a dynamically allocated string that holds the full path formed by the provided arguments. Both path prefix and file extension are optional.
/// If any elements from the generated path exceed safe filesystem limits, each exceeding element will be truncated. Truncations, if needed, are performed on a per-codepoint basis (UTF-8).
/// If an extension is provided, it will always be preserved, regardless of any possible truncations being carried out.
/// A path separator is automatically placed between the provided prefix and the filename if the prefix doesn't end with one.
/// A dot *isn't* automatically placed between the filename and the provided extension -- if required, it must be provided as part of the extension string.
/// Furthermore, if the full length for the generated path is >= FS_MAX_PATH, NULL will be returned.
/// The allocated buffer must be freed by the caller using free().
char *utilsGeneratePath(const char *prefix, const char *filename, const char *extension);
/// Prints an error message using the standard console output and waits for the user to press a button.
void utilsPrintConsoleError(const char *msg);
/// Returns the current application updated state.
bool utilsGetApplicationUpdatedState(void);
/// Sets the application updated state to true, which makes utilsCloseResources() replace the application NRO.
void utilsSetApplicationUpdatedState(void);
/// Parses the provided GitHub release JSON data buffer.
/// The data from the output buffer must be freed using utilsFreeGitHubReleaseJsonData().
bool utilsParseGitHubReleaseJsonData(const char *json_buf, size_t json_buf_size, UtilsGitHubReleaseJsonData *out);
/// Parses the provided version string and compares it to the application version. Returns true if the application can be updated.
/// If both versions are equal, the provided commit hash is compared to our commit hash - if they're different, true will be returned.
bool utilsIsApplicationUpdatable(const char *version, const char *commit_hash);
/// Frees previously allocated data from a UtilsGitHubReleaseJsonData element.
NX_INLINE void utilsFreeGitHubReleaseJsonData(UtilsGitHubReleaseJsonData *data)
{
if (!data) return;
if (data->obj) json_object_put(data->obj);
memset(data, 0, sizeof(UtilsGitHubReleaseJsonData));
}
/// Simple wrapper to sleep the current thread for a specific number of full seconds.
NX_INLINE void utilsSleep(u64 seconds)
{
if (seconds) svcSleepThread(seconds * (u64)1000000000);
}
/// Introduces a 33.33 milliseconds delay. Suitable to avoid hitting 100% CPU core usage in appletMainLoop() loops.
NX_INLINE void utilsAppletLoopDelay(void)
{
svcSleepThread(THIRTY_FPS_DELAY);
}
/// Wrappers used in scoped locks.
NX_INLINE UtilsScopedLock utilsLockScope(Mutex *mtx)
{
UtilsScopedLock scoped_lock = { mtx, !mutexIsLockedByCurrentThread(mtx), 1 };
if (scoped_lock.lock) mutexLock(scoped_lock.mtx);
return scoped_lock;
}
NX_INLINE UtilsScopedLock utilsTryLockScope(Mutex *mtx)
{
UtilsScopedLock scoped_lock = { mtx, !mutexIsLockedByCurrentThread(mtx), 1 };
if (scoped_lock.lock) scoped_lock.cond = (int)mutexTryLock(scoped_lock.mtx);
return scoped_lock;
}
NX_INLINE void utilsUnlockScope(UtilsScopedLock *scoped_lock)
{
if (scoped_lock->lock) mutexUnlock(scoped_lock->mtx);
}
#ifdef __cplusplus
}
#endif
#endif /* __NXDT_UTILS_H__ */

204
include/core/pfs.h Normal file
View File

@ -0,0 +1,204 @@
/*
* pfs.h
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __PFS_H__
#define __PFS_H__
#include "nca_storage.h"
#ifdef __cplusplus
extern "C" {
#endif
#define PFS0_MAGIC 0x50465330 /* "PFS0". */
typedef struct {
u32 magic; ///< "PFS0".
u32 entry_count;
u32 name_table_size;
u8 reserved[0x4];
} PartitionFileSystemHeader;
NXDT_ASSERT(PartitionFileSystemHeader, 0x10);
typedef struct {
u64 offset;
u64 size;
u32 name_offset;
u8 reserved[0x4];
} PartitionFileSystemEntry;
NXDT_ASSERT(PartitionFileSystemEntry, 0x18);
/// Used with Partition FS sections from NCAs.
typedef struct {
NcaStorageContext storage_ctx; ///< Used to read NCA FS section data.
NcaFsSectionContext *nca_fs_ctx; ///< Same as storage_ctx.nca_fs_ctx. Placed here for convenience.
u64 offset; ///< Partition offset (relative to the start of the NCA FS section).
u64 size; ///< Partition size.
bool is_exefs; ///< ExeFS flag.
u64 header_size; ///< Full header size.
u8 *header; ///< PartitionFileSystemHeader + (PartitionFileSystemEntry * entry_count) + Name Table.
} PartitionFileSystemContext;
/// Used to generate Partition FS images (e.g. NSPs).
typedef struct {
PartitionFileSystemHeader header; ///< Partition FS header. Holds the entry count and name table size.
PartitionFileSystemEntry *entries; ///< Partition FS entries.
char *name_table; ///< Name table.
u64 fs_size; ///< Partition FS data size. Updated each time a new entry is added.
} PartitionFileSystemImageContext;
/// Initializes a Partition FS context.
bool pfsInitializeContext(PartitionFileSystemContext *out, NcaFsSectionContext *nca_fs_ctx);
/// Reads raw partition data using a Partition FS context.
/// Input offset must be relative to the start of the Partition FS.
bool pfsReadPartitionData(PartitionFileSystemContext *ctx, void *out, u64 read_size, u64 offset);
/// Reads data from a previously retrieved PartitionFileSystemEntry using a Partition FS context.
/// Input offset must be relative to the start of the Partition FS entry.
bool pfsReadEntryData(PartitionFileSystemContext *ctx, PartitionFileSystemEntry *fs_entry, void *out, u64 read_size, u64 offset);
/// Retrieves a Partition FS entry index by its name.
bool pfsGetEntryIndexByName(PartitionFileSystemContext *ctx, const char *name, u32 *out_idx);
/// Calculates the extracted Partition FS size.
bool pfsGetTotalDataSize(PartitionFileSystemContext *ctx, u64 *out_size);
/// Generates HierarchicalSha256 FS section patch data using a Partition FS context + entry, which can be used to seamlessly replace NCA data.
/// Input offset must be relative to the start of the Partition FS entry data.
/// This function shares the same limitations as ncaGenerateHierarchicalSha256Patch().
/// Use the pfsWriteEntryPatchToMemoryBuffer() wrapper to write patch data generated by this function.
bool pfsGenerateEntryPatch(PartitionFileSystemContext *ctx, PartitionFileSystemEntry *fs_entry, const void *data, u64 data_size, u64 data_offset, NcaHierarchicalSha256Patch *out);
/// Adds a new Partition FS entry to an existing PartitionFileSystemImageContext, using the provided entry name and size.
/// If 'out_entry_idx' is a valid pointer, the index to the new Partition FS entry will be saved to it.
bool pfsAddEntryInformationToImageContext(PartitionFileSystemImageContext *ctx, const char *entry_name, u64 entry_size, u32 *out_entry_idx);
/// Updates the name from a Partition FS entry in an existing PartitionFileSystemImageContext, using an entry index and the new entry name.
bool pfsUpdateEntryNameFromImageContext(PartitionFileSystemImageContext *ctx, u32 entry_idx, const char *new_entry_name);
/// Generates a full Partition FS header from an existing PartitionFileSystemImageContext and writes it to the provided memory buffer.
bool pfsWriteImageContextHeaderToMemoryBuffer(PartitionFileSystemImageContext *ctx, void *buf, u64 buf_size, u64 *out_header_size);
/// Miscellaneous functions.
NX_INLINE void pfsFreeContext(PartitionFileSystemContext *ctx)
{
if (!ctx) return;
ncaStorageFreeContext(&(ctx->storage_ctx));
if (ctx->header) free(ctx->header);
memset(ctx, 0, sizeof(PartitionFileSystemContext));
}
/// Checks if the provided PartitionFileSystemContext is valid.
NX_INLINE bool pfsIsValidContext(PartitionFileSystemContext *ctx)
{
return (ctx && ncaStorageIsValidContext(&(ctx->storage_ctx)) && ctx->nca_fs_ctx == ctx->storage_ctx.nca_fs_ctx && \
ctx->storage_ctx.base_storage_type == NcaStorageBaseStorageType_Regular && ctx->size && ctx->header_size && ctx->header);
}
NX_INLINE u32 pfsGetEntryCount(PartitionFileSystemContext *ctx)
{
return (pfsIsValidContext(ctx) ? ((PartitionFileSystemHeader*)ctx->header)->entry_count : 0);
}
NX_INLINE PartitionFileSystemEntry *pfsGetEntryByIndex(PartitionFileSystemContext *ctx, u32 idx)
{
return (idx < pfsGetEntryCount(ctx) ? (PartitionFileSystemEntry*)(ctx->header + sizeof(PartitionFileSystemHeader) + (idx * sizeof(PartitionFileSystemEntry))) : NULL);
}
NX_INLINE char *pfsGetNameTable(PartitionFileSystemContext *ctx)
{
u32 entry_count = pfsGetEntryCount(ctx);
return (entry_count ? (char*)(ctx->header + sizeof(PartitionFileSystemHeader) + (entry_count * sizeof(PartitionFileSystemEntry))) : NULL);
}
NX_INLINE char *pfsGetEntryName(PartitionFileSystemContext *ctx, PartitionFileSystemEntry *fs_entry)
{
char *name_table = pfsGetNameTable(ctx);
if (!name_table || !fs_entry || fs_entry->name_offset >= ((PartitionFileSystemHeader*)ctx->header)->name_table_size || !name_table[fs_entry->name_offset]) return NULL;
return (name_table + fs_entry->name_offset);
}
NX_INLINE char *pfsGetEntryNameByIndex(PartitionFileSystemContext *ctx, u32 idx)
{
PartitionFileSystemEntry *fs_entry = pfsGetEntryByIndex(ctx, idx);
char *name_table = pfsGetNameTable(ctx);
return ((fs_entry && name_table) ? (name_table + fs_entry->name_offset) : NULL);
}
NX_INLINE PartitionFileSystemEntry *pfsGetEntryByName(PartitionFileSystemContext *ctx, const char *name)
{
u32 idx = 0;
return (pfsGetEntryIndexByName(ctx, name, &idx) ? pfsGetEntryByIndex(ctx, idx) : NULL);
}
NX_INLINE void pfsWriteEntryPatchToMemoryBuffer(PartitionFileSystemContext *ctx, NcaHierarchicalSha256Patch *patch, void *buf, u64 buf_size, u64 buf_offset)
{
if (!pfsIsValidContext(ctx)) return;
ncaWriteHierarchicalSha256PatchToMemoryBuffer(ctx->nca_fs_ctx->nca_ctx, patch, buf, buf_size, buf_offset);
}
NX_INLINE void pfsFreeEntryPatch(NcaHierarchicalSha256Patch *patch)
{
ncaFreeHierarchicalSha256Patch(patch);
}
NX_INLINE void pfsFreeImageContext(PartitionFileSystemImageContext *ctx)
{
if (!ctx) return;
if (ctx->entries) free(ctx->entries);
if (ctx->name_table) free(ctx->name_table);
memset(ctx, 0, sizeof(PartitionFileSystemImageContext));
}
NX_INLINE void pfsInitializeImageContext(PartitionFileSystemImageContext *ctx)
{
if (!ctx) return;
pfsFreeImageContext(ctx);
ctx->header.magic = __builtin_bswap32(PFS0_MAGIC);
}
NX_INLINE u32 pfsGetEntryCountFromImageContext(PartitionFileSystemImageContext *ctx)
{
return (ctx ? ctx->header.entry_count : 0);
}
NX_INLINE PartitionFileSystemEntry *pfsGetEntryByIndexFromImageContext(PartitionFileSystemImageContext *ctx, u32 idx)
{
return (idx < pfsGetEntryCountFromImageContext(ctx) ? &(ctx->entries[idx]) : NULL);
}
NX_INLINE char *pfsGetEntryNameByIndexFromImageContext(PartitionFileSystemImageContext *ctx, u32 idx)
{
PartitionFileSystemEntry *fs_entry = pfsGetEntryByIndexFromImageContext(ctx, idx);
return ((fs_entry && ctx->name_table) ? (ctx->name_table + fs_entry->name_offset) : NULL);
}
#ifdef __cplusplus
}
#endif
#endif /* __PFS_H__ */

View File

@ -0,0 +1,81 @@
/*
* program_info.h
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __PROGRAM_INFO_H__
#define __PROGRAM_INFO_H__
#include "npdm.h"
#include "nso.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
NcaContext *nca_ctx; ///< Pointer to the NCA context for the Program NCA from which program data (NPDM / NSO) is retrieved.
PartitionFileSystemContext pfs_ctx; ///< PartitionFileSystemContext for the Program NCA ExeFS, which is where program data (NPDM / NSO) is stored.
NpdmContext npdm_ctx; ///< NpdmContext for the NPDM stored in Program NCA ExeFS.
u32 nso_count; ///< Number of NSOs stored in Program NCA FS section #0.
NsoContext *nso_ctx; ///< Pointer to a dynamically allocated buffer that holds 'nso_count' NSO contexts.
char *authoring_tool_xml; ///< Pointer to a dynamically allocated, NULL-terminated buffer that holds AuthoringTool-like XML data.
///< This is always NULL unless programInfoGenerateAuthoringToolXml() is used on this ProgramInfoContext.
u64 authoring_tool_xml_size; ///< Size for the AuthoringTool-like XML. This is essentially the same as using strlen() on 'authoring_tool_xml'.
///< This is always 0 unless programInfoGenerateAuthoringToolXml() is used on this ProgramInfoContext.
} ProgramInfoContext;
/// Initializes a ProgramInfoContext using a previously initialized NcaContext (which must belong to a Program NCA).
bool programInfoInitializeContext(ProgramInfoContext *out, NcaContext *nca_ctx);
/// Generates an AuthoringTool-like XML using information from a previously initialized ProgramInfoContext.
/// If the function succeeds, XML data and size will get saved to the 'authoring_tool_xml' and 'authoring_tool_xml_size' members from the ProgramInfoContext.
bool programInfoGenerateAuthoringToolXml(ProgramInfoContext *program_info_ctx);
/// Helper inline functions.
NX_INLINE void programInfoFreeContext(ProgramInfoContext *program_info_ctx)
{
if (!program_info_ctx) return;
pfsFreeContext(&(program_info_ctx->pfs_ctx));
npdmFreeContext(&(program_info_ctx->npdm_ctx));
if (program_info_ctx->nso_ctx)
{
for(u32 i = 0; i < program_info_ctx->nso_count; i++) nsoFreeContext(&(program_info_ctx->nso_ctx[i]));
free(program_info_ctx->nso_ctx);
}
if (program_info_ctx->authoring_tool_xml) free(program_info_ctx->authoring_tool_xml);
memset(program_info_ctx, 0, sizeof(ProgramInfoContext));
}
NX_INLINE bool programInfoIsValidContext(ProgramInfoContext *program_info_ctx)
{
return (program_info_ctx && program_info_ctx->nca_ctx && npdmIsValidContext(&(program_info_ctx->npdm_ctx)) && program_info_ctx->nso_count && program_info_ctx->nso_ctx);
}
#ifdef __cplusplus
}
#endif
#endif /* __PROGRAM_INFO_H__ */

258
include/core/romfs.h Normal file
View File

@ -0,0 +1,258 @@
/*
* romfs.h
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __ROMFS_H__
#define __ROMFS_H__
#include "nca_storage.h"
#ifdef __cplusplus
extern "C" {
#endif
#define ROMFS_OLD_HEADER_SIZE 0x28
#define ROMFS_HEADER_SIZE 0x50
#define ROMFS_VOID_ENTRY UINT32_MAX
#define ROMFS_TABLE_ENTRY_ALIGNMENT 0x4
/// Header used by NCA0 RomFS sections.
typedef struct {
u32 header_size; ///< Header size. Must be equal to ROMFS_OLD_HEADER_SIZE.
u32 directory_bucket_offset; ///< Directory bucket offset.
u32 directory_bucket_size; ///< Directory bucket size.
u32 directory_entry_offset; ///< Directory entries table offset.
u32 directory_entry_size; ///< Directory entries table size.
u32 file_bucket_offset; ///< File bucket offset.
u32 file_bucket_size; ///< File bucket size.
u32 file_entry_offset; ///< File entries table offset.
u32 file_entry_size; ///< File entries table size.
u32 body_offset; ///< File data body offset.
} RomFileSystemInformationOld;
NXDT_ASSERT(RomFileSystemInformationOld, ROMFS_OLD_HEADER_SIZE);
/// Header used by NCA2/NCA3 RomFS sections.
typedef struct {
u64 header_size; ///< Header size. Must be equal to ROMFS_HEADER_SIZE.
u64 directory_bucket_offset; ///< Directory bucket offset.
u64 directory_bucket_size; ///< Directory bucket size.
u64 directory_entry_offset; ///< Directory entries table offset.
u64 directory_entry_size; ///< Directory entries table size.
u64 file_bucket_offset; ///< File bucket offset.
u64 file_bucket_size; ///< File bucket size.
u64 file_entry_offset; ///< File entries table offset.
u64 file_entry_size; ///< File entries table size.
u64 body_offset; ///< File data body offset.
} RomFileSystemInformation;
NXDT_ASSERT(RomFileSystemInformation, ROMFS_HEADER_SIZE);
/// Header union.
typedef struct {
union {
struct {
RomFileSystemInformationOld old_format;
u8 padding[ROMFS_OLD_HEADER_SIZE];
};
RomFileSystemInformation cur_format;
};
} RomFileSystemHeader;
NXDT_ASSERT(RomFileSystemHeader, ROMFS_HEADER_SIZE);
/// Directory entry. Always aligned to a ROMFS_TABLE_ENTRY_ALIGNMENT boundary past the directory name.
typedef struct {
u32 parent_offset; ///< Parent directory offset.
u32 next_offset; ///< Next sibling directory offset. May be set to ROMFS_VOID_ENTRY if there are no other directory entries at this level.
u32 directory_offset; ///< First child directory offset. May be set to ROMFS_VOID_ENTRY if there are no child directories entries.
u32 file_offset; ///< First child file offset. May be set to ROMFS_VOID_ENTRY if there are no child file entries.
u32 bucket_offset; ///< Directory bucket offset.
u32 name_length; ///< Name length.
char name[]; ///< Name (UTF-8, may not be NULL terminated depending on the whole entry alignment).
} RomFileSystemDirectoryEntry;
NXDT_ASSERT(RomFileSystemDirectoryEntry, 0x18);
/// Directory entry. Always aligned to a ROMFS_TABLE_ENTRY_ALIGNMENT boundary past the file name.
typedef struct {
u32 parent_offset; ///< Parent directory offset.
u32 next_offset; ///< Next sibling file offset. May be set to ROMFS_VOID_ENTRY if there are no other file entries at this level.
u64 offset; ///< File data offset.
u64 size; ///< File data size.
u32 bucket_offset; ///< File bucket offset.
u32 name_length; ///< Name length.
char name[]; ///< Name (UTF-8, may not be NULL terminated depending on the whole entry alignment).
} RomFileSystemFileEntry;
NXDT_ASSERT(RomFileSystemFileEntry, 0x20);
typedef struct {
bool is_patch; ///< Set to true if this we're dealing with a Patch RomFS.
NcaStorageContext storage_ctx[2]; ///< Used to read NCA FS section data. Index 0: base storage. Index 1: patch storage.
NcaStorageContext *default_storage_ctx; ///< Default NCA storage context. Points to one of the two contexts from 'storage_ctx'. Placed here for convenience.
u64 offset; ///< RomFS offset (relative to the start of the NCA FS section).
u64 size; ///< RomFS size.
RomFileSystemHeader header; ///< RomFS header.
u64 dir_bucket_size; ///< RomFS directory bucket size.
u32 *dir_bucket; ///< RomFS directory bucket.
u64 dir_table_size; ///< RomFS directory entries table size.
RomFileSystemDirectoryEntry *dir_table; ///< RomFS directory entries table.
u64 file_bucket_size; ///< RomFS file bucket size.
u32 *file_bucket; ///< RomFS file bucket.
u64 file_table_size; ///< RomFS file entries table size.
RomFileSystemFileEntry *file_table; ///< RomFS file entries table.
u64 body_offset; ///< RomFS file data body offset (relative to the start of the RomFS).
} RomFileSystemContext;
typedef struct {
bool use_old_format_patch; ///< Old format patch flag.
bool written; ///< Set to true if the patch has been completely written.
NcaHierarchicalSha256Patch old_format_patch; ///< Used with NCA0 RomFS sections.
NcaHierarchicalIntegrityPatch cur_format_patch; ///< Used with NCA2/NCA3 RomFS sections.
} RomFileSystemFileEntryPatch;
typedef enum {
RomFileSystemPathIllegalCharReplaceType_None = 0,
RomFileSystemPathIllegalCharReplaceType_IllegalFsChars = 1,
RomFileSystemPathIllegalCharReplaceType_KeepAsciiCharsOnly = 2,
RomFileSystemPathIllegalCharReplaceType_Count = 3 ///< Total values supported by this enum.
} RomFileSystemPathIllegalCharReplaceType;
/// Initializes a RomFS or Patch RomFS context.
/// 'base_nca_fs_ctx' shall be NULL *only* if a NCA from an update has no matching equivalent available in its base title.
/// 'patch_nca_fs_ctx' shall be NULL if not dealing with a Patch RomFS.
bool romfsInitializeContext(RomFileSystemContext *out, NcaFsSectionContext *base_nca_fs_ctx, NcaFsSectionContext *patch_nca_fs_ctx);
/// Reads raw filesystem data using a RomFS context.
/// Input offset must be relative to the start of the RomFS.
bool romfsReadFileSystemData(RomFileSystemContext *ctx, void *out, u64 read_size, u64 offset);
/// Reads data from a previously retrieved RomFileSystemFileEntry using a RomFS context.
/// Input offset must be relative to the start of the RomFS file entry data.
bool romfsReadFileEntryData(RomFileSystemContext *ctx, RomFileSystemFileEntry *file_entry, void *out, u64 read_size, u64 offset);
/// Calculates the extracted RomFS size.
/// If 'only_updated' is set to true and the provided RomFS context was initialized as a Patch RomFS context, only files modified by the update will be considered.
bool romfsGetTotalDataSize(RomFileSystemContext *ctx, bool only_updated, u64 *out_size);
/// Calculates the extracted size from a RomFS directory.
bool romfsGetDirectoryDataSize(RomFileSystemContext *ctx, RomFileSystemDirectoryEntry *dir_entry, u64 *out_size);
/// Retrieves a RomFS directory entry by path.
/// Input path must have a leading slash ('/'). If just a single slash is provided, a pointer to the root directory entry shall be returned.
RomFileSystemDirectoryEntry *romfsGetDirectoryEntryByPath(RomFileSystemContext *ctx, const char *path);
/// Retrieves a RomFS file entry by path.
/// Input path must have a leading slash ('/').
RomFileSystemFileEntry *romfsGetFileEntryByPath(RomFileSystemContext *ctx, const char *path);
/// Generates a path string from a RomFS directory entry.
bool romfsGeneratePathFromDirectoryEntry(RomFileSystemContext *ctx, RomFileSystemDirectoryEntry *dir_entry, char *out_path, size_t out_path_size, u8 illegal_char_replace_type);
/// Generates a path string from a RomFS file entry.
bool romfsGeneratePathFromFileEntry(RomFileSystemContext *ctx, RomFileSystemFileEntry *file_entry, char *out_path, size_t out_path_size, u8 illegal_char_replace_type);
/// Checks if a RomFS file entry is updated by the Patch RomFS.
/// Only works if the provided RomFileSystemContext was initialized as a Patch RomFS context.
bool romfsIsFileEntryUpdated(RomFileSystemContext *ctx, RomFileSystemFileEntry *file_entry, bool *out);
/// Generates HierarchicalSha256 (NCA0) / HierarchicalIntegrity (NCA2/NCA3) FS section patch data using a RomFS context + file entry, which can be used to seamlessly replace NCA data.
/// Input offset must be relative to the start of the RomFS file entry data.
/// This function shares the same limitations as ncaGenerateHierarchicalSha256Patch() / ncaGenerateHierarchicalIntegrityPatch().
/// Use the romfsWriteFileEntryPatchToMemoryBuffer() wrapper to write patch data generated by this function.
bool romfsGenerateFileEntryPatch(RomFileSystemContext *ctx, RomFileSystemFileEntry *file_entry, const void *data, u64 data_size, u64 data_offset, RomFileSystemFileEntryPatch *out);
/// Resets a previously initialized RomFileSystemContext.
NX_INLINE void romfsFreeContext(RomFileSystemContext *ctx)
{
if (!ctx) return;
ncaStorageFreeContext(&(ctx->storage_ctx[0]));
ncaStorageFreeContext(&(ctx->storage_ctx[1]));
if (ctx->dir_bucket) free(ctx->dir_bucket);
if (ctx->dir_table) free(ctx->dir_table);
if (ctx->file_bucket) free(ctx->file_bucket);
if (ctx->file_table) free(ctx->file_table);
memset(ctx, 0, sizeof(RomFileSystemContext));
}
/// Checks if the provided RomFileSystemContext is valid.
NX_INLINE bool romfsIsValidContext(RomFileSystemContext *ctx)
{
return (ctx && ncaStorageIsValidContext(ctx->default_storage_ctx) && ctx->size && ctx->dir_bucket_size && ctx->dir_bucket && ctx->dir_table_size && ctx->dir_table && \
ctx->file_bucket_size && ctx->file_bucket && ctx->file_table_size && ctx->file_table && ctx->body_offset >= ctx->header.old_format.header_size && \
ctx->body_offset < ctx->size);
}
/// Functions to retrieve a directory/file entry.
NX_INLINE void *romfsGetEntryByOffset(RomFileSystemContext *ctx, void *entry_table, u64 entry_table_size, u64 entry_size, u64 entry_offset)
{
if (!romfsIsValidContext(ctx) || !entry_table || !entry_table_size || !entry_size || (entry_offset + entry_size) > entry_table_size) return NULL;
return ((u8*)entry_table + entry_offset);
}
NX_INLINE RomFileSystemDirectoryEntry *romfsGetDirectoryEntryByOffset(RomFileSystemContext *ctx, u64 dir_entry_offset)
{
return (ctx ? (RomFileSystemDirectoryEntry*)romfsGetEntryByOffset(ctx, ctx->dir_table, ctx->dir_table_size, sizeof(RomFileSystemDirectoryEntry), dir_entry_offset) : NULL);
}
NX_INLINE RomFileSystemFileEntry *romfsGetFileEntryByOffset(RomFileSystemContext *ctx, u64 file_entry_offset)
{
return (ctx ? (RomFileSystemFileEntry*)romfsGetEntryByOffset(ctx, ctx->file_table, ctx->file_table_size, sizeof(RomFileSystemFileEntry), file_entry_offset) : NULL);
}
/// NCA patch management functions.
NX_INLINE void romfsWriteFileEntryPatchToMemoryBuffer(RomFileSystemContext *ctx, RomFileSystemFileEntryPatch *patch, void *buf, u64 buf_size, u64 buf_offset)
{
if (!romfsIsValidContext(ctx) || ctx->is_patch || ctx->default_storage_ctx->base_storage_type != NcaStorageBaseStorageType_Regular || !patch || \
(!patch->use_old_format_patch && ctx->default_storage_ctx->nca_fs_ctx->section_type != NcaFsSectionType_RomFs) || \
(patch->use_old_format_patch && ctx->default_storage_ctx->nca_fs_ctx->section_type != NcaFsSectionType_Nca0RomFs)) return;
NcaContext *nca_ctx = ctx->default_storage_ctx->nca_fs_ctx->nca_ctx;
if (patch->use_old_format_patch)
{
ncaWriteHierarchicalSha256PatchToMemoryBuffer(nca_ctx, &(patch->old_format_patch), buf, buf_size, buf_offset);
patch->written = patch->old_format_patch.written;
} else {
ncaWriteHierarchicalIntegrityPatchToMemoryBuffer(nca_ctx, &(patch->cur_format_patch), buf, buf_size, buf_offset);
patch->written = patch->cur_format_patch.written;
}
}
NX_INLINE void romfsFreeFileEntryPatch(RomFileSystemFileEntryPatch *patch)
{
if (!patch) return;
ncaFreeHierarchicalSha256Patch(&(patch->old_format_patch));
ncaFreeHierarchicalIntegrityPatch(&(patch->cur_format_patch));
memset(patch, 0, sizeof(RomFileSystemFileEntryPatch));
}
#ifdef __cplusplus
}
#endif
#endif /* __ROMFS_H__ */

59
include/core/rsa.h Normal file
View File

@ -0,0 +1,59 @@
/*
* rsa.c
*
* Copyright (c) 2018-2019, SciresM.
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __RSA_H__
#define __RSA_H__
#ifdef __cplusplus
extern "C" {
#endif
#define RSA2048_BYTES 0x100
#define RSA2048_BITS (RSA2048_BYTES * 8)
#define RSA2048_SIG_SIZE RSA2048_BYTES
#define RSA2048_PUBKEY_SIZE RSA2048_BYTES
/// Verifies a RSA-2048-PSS with SHA-256 signature.
/// Suitable for NCA and NPDM signatures.
/// The provided signature and modulus must have sizes of at least RSA2048_SIG_SIZE and RSA2048_PUBKEY_SIZE, respectively.
bool rsa2048VerifySha256BasedPssSignature(const void *data, size_t data_size, const void *signature, const void *modulus, const void *public_exponent, size_t public_exponent_size);
/// Verifies a RSA-2048-PKCS#1 v1.5 with SHA-256 signature.
/// Suitable for ticket and certificate chain signatures.
/// The provided signature and modulus must have sizes of at least RSA2048_SIG_SIZE and RSA2048_PUBKEY_SIZE, respectively.
bool rsa2048VerifySha256BasedPkcs1v15Signature(const void *data, size_t data_size, const void *signature, const void *modulus, const void *public_exponent, size_t public_exponent_size);
/// Performs RSA-2048-OAEP decryption.
/// Suitable to decrypt the titlekey block from personalized tickets.
/// The provided signature and modulus must have sizes of at least RSA2048_SIG_SIZE and RSA2048_PUBKEY_SIZE, respectively.
/// 'label' and 'label_size' arguments are optional -- if not needed, these may be set to NULL and 0, respectively.
bool rsa2048OaepDecrypt(void *dst, size_t dst_size, const void *signature, const void *modulus, const void *public_exponent, size_t public_exponent_size, const void *private_exponent, \
size_t private_exponent_size, const void *label, size_t label_size, size_t *out_size);
#ifdef __cplusplus
}
#endif
#endif /* __RSA_H__ */

View File

@ -1,14 +1,37 @@
/*
* save.h
*
* Copyright (c) 2019-2020, shchmue.
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef _SAVE_H
#define _SAVE_H
#include <stddef.h>
#include <stdint.h>
#include <switch.h>
#ifndef __SAVE_H__
#define __SAVE_H__
#include "fatfs/ff.h"
#include "nca.h"
#ifdef __cplusplus
extern "C" {
#endif
#define IVFC_MAX_LEVEL 6
#define SAVE_HEADER_SIZE 0x4000
#define SAVE_FAT_ENTRY_SIZE 8
@ -33,7 +56,7 @@ typedef enum {
typedef struct save_ctx_t save_ctx_t;
typedef struct {
u32 magic; /* DISF */
u32 magic; /* "DISF". */
u32 version;
u8 hash[0x20];
u64 file_map_entry_offset;
@ -84,18 +107,26 @@ typedef struct {
u8 _0x190[0x70];
} fs_layout_t;
NXDT_ASSERT(fs_layout_t, 0x200);
#pragma pack(push, 1)
typedef struct {
u64 offset;
u64 length;
u32 block_size_power;
} PACKED duplex_info_t;
} duplex_info_t;
#pragma pack(pop)
NXDT_ASSERT(duplex_info_t, 0x14);
typedef struct {
u32 magic; /* DPFS */
u32 magic; /* "DPFS". */
u32 version;
duplex_info_t layers[3];
} duplex_header_t;
NXDT_ASSERT(duplex_header_t, 0x44);
typedef struct {
u32 version;
u32 main_data_block_count;
@ -103,21 +134,27 @@ typedef struct {
u32 _0x0C;
} journal_map_header_t;
NXDT_ASSERT(journal_map_header_t, 0x10);
typedef struct {
u32 magic; /* JNGL */
u32 magic; /* "JNGL". */
u32 version;
u64 total_size;
u64 journal_size;
u64 block_size;
} journal_header_t;
NXDT_ASSERT(journal_header_t, 0x20);
typedef struct {
u32 magic; /* SAVE */
u32 magic; /* "SAVE". */
u32 version;
u64 block_count;
u64 block_size;
} save_fs_header_t;
NXDT_ASSERT(save_fs_header_t, 0x18);
typedef struct {
u64 block_size;
u64 allocation_table_offset;
@ -130,8 +167,10 @@ typedef struct {
u32 file_table_block;
} fat_header_t;
NXDT_ASSERT(fat_header_t, 0x30);
typedef struct {
u32 magic; /* RMAP */
u32 magic; /* "RMAP". */
u32 version;
u32 map_entry_count;
u32 map_segment_count;
@ -139,9 +178,12 @@ typedef struct {
u8 _0x14[0x2C];
} remap_header_t;
NXDT_ASSERT(remap_header_t, 0x40);
typedef struct remap_segment_ctx_t remap_segment_ctx_t;
typedef struct remap_entry_ctx_t remap_entry_ctx_t;
#pragma pack(push, 1)
struct remap_entry_ctx_t {
u64 virtual_offset;
u64 physical_offset;
@ -152,12 +194,13 @@ struct remap_entry_ctx_t {
u64 physical_offset_end;
remap_segment_ctx_t *segment;
remap_entry_ctx_t *next;
} PACKED;
};
#pragma pack(pop)
struct remap_segment_ctx_t{
u64 offset;
u64 length;
remap_entry_ctx_t *entries;
remap_entry_ctx_t **entries;
u64 entry_count;
};
@ -206,6 +249,17 @@ typedef struct {
u64 commit_id;
} extra_data_t;
NXDT_ASSERT(extra_data_t, 0x70);
typedef struct {
u64 logical_offset;
u64 hash_data_size;
u32 block_size;
u32 reserved;
} ivfc_level_hdr_t;
NXDT_ASSERT(ivfc_level_hdr_t, 0x18);
typedef struct {
u32 magic;
u32 id;
@ -215,6 +269,9 @@ typedef struct {
u8 salt_source[0x20];
} ivfc_save_hdr_t;
NXDT_ASSERT(ivfc_save_hdr_t, 0xC0);
#pragma pack(push, 1)
typedef struct {
u8 cmac[0x10];
u8 _0x10[0xF0];
@ -233,7 +290,10 @@ typedef struct {
u8 _0x748[0x390];
ivfc_save_hdr_t fat_ivfc_header;
u8 _0xB98[0x3468];
} PACKED save_header_t;
} save_header_t;
#pragma pack(pop)
NXDT_ASSERT(save_header_t, 0x4000);
typedef struct {
duplex_storage_ctx_t layers[2];
@ -259,6 +319,8 @@ typedef struct {
u32 virtual_index;
} journal_map_entry_t;
NXDT_ASSERT(journal_map_entry_t, 0x8);
typedef struct {
journal_map_header_t *header;
journal_map_entry_t *entries;
@ -344,32 +406,48 @@ typedef struct {
u32 parent;
} save_entry_key_t;
#pragma pack(push, 1)
typedef struct {
u32 start_block;
u64 length;
u32 _0xC[2];
} PACKED save_file_info_t;
} save_file_info_t;
#pragma pack(pop)
NXDT_ASSERT(save_file_info_t, 0x14);
#pragma pack(push, 1)
typedef struct {
u32 next_directory;
u32 next_file;
u32 _0x8[3];
} PACKED save_find_position_t;
} save_find_position_t;
#pragma pack(pop)
NXDT_ASSERT(save_find_position_t, 0x14);
#pragma pack(push, 1)
typedef struct {
u32 next_sibling;
union { /* Save table entry type. Size = 0x14. */
save_file_info_t save_file_info;
save_find_position_t save_find_position;
};
} PACKED save_table_entry_t;
} save_table_entry_t;
#pragma pack(pop)
NXDT_ASSERT(save_table_entry_t, 0x18);
#pragma pack(push, 1)
typedef struct {
u32 parent;
char name[SAVE_FS_LIST_MAX_NAME_LENGTH];
save_table_entry_t value;
u32 next;
} PACKED save_fs_list_entry_t;
} save_fs_list_entry_t;
#pragma pack(pop)
NXDT_ASSERT(save_fs_list_entry_t, 0x60);
typedef struct {
u32 free_list_head_index;
@ -466,10 +544,16 @@ void save_free_contexts(save_ctx_t *ctx);
bool save_open_fat_storage(save_filesystem_ctx_t *ctx, allocation_table_storage_ctx_t *storage_ctx, u32 block_index);
u32 save_allocation_table_storage_read(allocation_table_storage_ctx_t *ctx, void *buffer, u64 offset, size_t count);
bool save_fs_list_get_value(save_filesystem_list_ctx_t *ctx, u32 index, save_fs_list_entry_t *value);
u32 save_fs_get_index_from_key(save_filesystem_list_ctx_t *ctx, save_entry_key_t *key, u32 *prev_index);
u32 save_fs_list_get_index_from_key(save_filesystem_list_ctx_t *ctx, save_entry_key_t *key, u32 *prev_index);
bool save_hierarchical_file_table_find_path_recursive(hierarchical_save_file_table_ctx_t *ctx, save_entry_key_t *key, const char *path);
bool save_hierarchical_file_table_get_file_entry_by_path(hierarchical_save_file_table_ctx_t *ctx, const char *path, save_fs_list_entry_t *entry);
bool retrieveCertData(u8 *out_cert, bool personalized);
save_ctx_t *save_open_savefile(const char *path, u32 action);
void save_close_savefile(save_ctx_t *ctx);
bool save_get_fat_storage_from_file_entry_by_path(save_ctx_t *ctx, const char *path, allocation_table_storage_ctx_t *out_fat_storage, u64 *out_file_entry_size);
#ifdef __cplusplus
}
#endif
#endif /* __SAVE_H__ */

View File

@ -1,5 +1,29 @@
/*
* service_guard.h
*
* Copyright (c) 2018-2020, Switchbrew and libnx contributors.
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#ifndef __SERVICE_GUARD_H__
#define __SERVICE_GUARD_H__
typedef struct ServiceGuard {
Mutex mutex;
@ -32,7 +56,7 @@ NX_INLINE void serviceGuardExit(ServiceGuard* g, void (*cleanupFunc)(void))
#define NX_GENERATE_SERVICE_GUARD_PARAMS(name, _paramdecl, _parampass) \
\
static ServiceGuard g_##name##Guard; \
static ServiceGuard g_##name##Guard = {0}; \
NX_INLINE Result _##name##Initialize _paramdecl; \
static void _##name##Cleanup(void); \
\
@ -50,3 +74,5 @@ void name##Exit(void) \
}
#define NX_GENERATE_SERVICE_GUARD(name) NX_GENERATE_SERVICE_GUARD_PARAMS(name, (void), ())
#endif /* __SERVICE_GUARD_H__ */

58
include/core/services.h Normal file
View File

@ -0,0 +1,58 @@
/*
* services.h
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __SERVICES_H__
#define __SERVICES_H__
#ifdef __cplusplus
extern "C" {
#endif
/* Hardware clocks expressed in MHz. */
#define CPU_CLKRT_NORMAL 1020
#define CPU_CLKRT_OVERCLOCKED 1785
#define MEM_CLKRT_NORMAL 1331
#define MEM_CLKRT_OVERCLOCKED 1600
/// Initializes the background services needed by the application.
bool servicesInitialize();
/// Closes services previously initialized by servicesInitialize().
void servicesClose();
/// Check if a service has been initialized by this interface using its name.
bool servicesCheckInitializedServiceByName(const char *name);
/// Checks if a service is running using its name, even if it wasn't initialized by this interface.
/// This servers as a wrapper for the "AtmosphereHasService" SM API extension from Atmosphère and Atmosphère-based CFWs.
/// Perfectly safe to use under development units. Not available in older Atmosphère releases.
bool servicesCheckRunningServiceByName(const char *name);
/// Changes CPU/MEM clock rates at runtime.
void servicesChangeHardwareClockRates(u32 cpu_rate, u32 mem_rate);
#ifdef __cplusplus
}
#endif
#endif /* __SERVICES_H__ */

84
include/core/sha3.h Normal file
View File

@ -0,0 +1,84 @@
/*
* sha3.h
*
* Copyright (c) Atmosphère-NX.
* Copyright (c) 2023-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
* Loosely based on crypto_sha3_impl.hpp from Atmosphere-libs.
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __SHA3_H__
#define __SHA3_H__
#include <switch.h>
#ifdef __cplusplus
extern "C" {
#endif
#ifndef SHA3_INTERNAL_STATE_SIZE
#define SHA3_INTERNAL_STATE_SIZE 200
#endif
#ifndef SHA3_HASH_SIZE_BYTES
#define SHA3_HASH_SIZE_BYTES(bits) ((bits) / 8)
#endif
#ifndef SHA3_BLOCK_SIZE
#define SHA3_BLOCK_SIZE(bits) (SHA3_INTERNAL_STATE_SIZE - (2 * SHA3_HASH_SIZE_BYTES(bits)))
#endif
#define _SHA3_CTX_OPS(bits) \
void sha3##bits##ContextCreate(Sha3Context *out); \
void sha3##bits##CalculateHash(void *dst, const void *src, size_t size);
/// Context for SHA3 operations.
typedef struct {
size_t hash_size;
size_t block_size;
size_t buffered_bytes;
u64 internal_state[SHA3_INTERNAL_STATE_SIZE / sizeof(u64)];
bool finalized;
} Sha3Context;
/// SHA3-224 context creation and simple all-in-one calculation functions.
_SHA3_CTX_OPS(224);
/// SHA3-256 context creation and simple all-in-one calculation functions.
_SHA3_CTX_OPS(256);
/// SHA3-384 context creation and simple all-in-one calculation functions.
_SHA3_CTX_OPS(384);
/// SHA3-512 context creation and simple all-in-one calculation functions.
_SHA3_CTX_OPS(512);
/// Updates SHA3 context with data to hash.
void sha3ContextUpdate(Sha3Context *ctx, const void *src, size_t size);
/// Gets the context's output hash, finalizes the context.
void sha3ContextGetHash(Sha3Context *ctx, void *dst);
#undef _SHA3_CTX_OPS
#ifdef __cplusplus
}
#endif
#endif /* __SHA3_H__ */

125
include/core/signature.h Normal file
View File

@ -0,0 +1,125 @@
/*
* signature.h
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __SIGNATURE_H__
#define __SIGNATURE_H__
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
SignatureType_Rsa4096Sha1 = 0x10000, ///< RSA-4096 PKCS#1 v1.5 + SHA-1.
SignatureType_Rsa2048Sha1 = 0x10001, ///< RSA-2048 PKCS#1 v1.5 + SHA-1.
SignatureType_Ecc480Sha1 = 0x10002, ///< Unpadded ECDSA + SHA-1.
SignatureType_Rsa4096Sha256 = 0x10003, ///< RSA-4096 PKCS#1 v1.5 + SHA-256.
SignatureType_Rsa2048Sha256 = 0x10004, ///< RSA-2048 PKCS#1 v1.5 + SHA-256.
SignatureType_Ecc480Sha256 = 0x10005, ///< Unpadded ECDSA + SHA-256.
SignatureType_Hmac160Sha1 = 0x10006 ///< HMAC-SHA1-160.
} SignatureType;
typedef struct {
u32 sig_type; ///< SignatureType_Rsa4096Sha1, SignatureType_Rsa4096Sha256.
u8 signature[0x200];
u8 padding[0x3C];
} SignatureBlockRsa4096;
NXDT_ASSERT(SignatureBlockRsa4096, 0x240);
typedef struct {
u32 sig_type; ///< SignatureType_Rsa2048Sha1, SignatureType_Rsa2048Sha256.
u8 signature[0x100];
u8 padding[0x3C];
} SignatureBlockRsa2048;
NXDT_ASSERT(SignatureBlockRsa2048, 0x140);
typedef struct {
u32 sig_type; ///< SignatureType_Ecc480Sha1, SignatureType_Ecc480Sha256.
u8 signature[0x3C];
u8 padding[0x40];
} SignatureBlockEcc480;
NXDT_ASSERT(SignatureBlockEcc480, 0x80);
typedef struct {
u32 sig_type; ///< SignatureType_Hmac160Sha1.
u8 signature[0x14];
u8 padding[0x28];
} SignatureBlockHmac160;
NXDT_ASSERT(SignatureBlockHmac160, 0x40);
/// Helper inline functions.
NX_INLINE bool signatureIsValidType(u32 type)
{
return (type == SignatureType_Rsa4096Sha1 || type == SignatureType_Rsa2048Sha1 || type == SignatureType_Ecc480Sha1 || \
type == SignatureType_Rsa4096Sha256 || type == SignatureType_Rsa2048Sha256 || type == SignatureType_Ecc480Sha256 || \
type == SignatureType_Hmac160Sha1);
}
NX_INLINE u64 signatureGetSigSizeByType(u32 type)
{
return (u64)((type == SignatureType_Rsa4096Sha1 || type == SignatureType_Rsa4096Sha256) ? MEMBER_SIZE(SignatureBlockRsa4096, signature) : \
((type == SignatureType_Rsa2048Sha1 || type == SignatureType_Rsa2048Sha256) ? MEMBER_SIZE(SignatureBlockRsa2048, signature) : \
((type == SignatureType_Ecc480Sha1 || type == SignatureType_Ecc480Sha256) ? MEMBER_SIZE(SignatureBlockEcc480, signature) : \
(type == SignatureType_Hmac160Sha1 ? MEMBER_SIZE(SignatureBlockHmac160, signature) : 0))));
}
NX_INLINE u64 signatureGetBlockSizeByType(u32 type)
{
return (u64)((type == SignatureType_Rsa4096Sha1 || type == SignatureType_Rsa4096Sha256) ? sizeof(SignatureBlockRsa4096) : \
((type == SignatureType_Rsa2048Sha1 || type == SignatureType_Rsa2048Sha256) ? sizeof(SignatureBlockRsa2048) : \
((type == SignatureType_Ecc480Sha1 || type == SignatureType_Ecc480Sha256) ? sizeof(SignatureBlockEcc480) : \
(type == SignatureType_Hmac160Sha1 ? sizeof(SignatureBlockHmac160) : 0))));
}
NX_INLINE u32 signatureGetTypeFromSignedBlob(void *buf, bool big_endian_sig_type)
{
if (!buf) return 0;
return (big_endian_sig_type ? __builtin_bswap32(*((u32*)buf)) : *((u32*)buf));
}
NX_INLINE u8 *signatureGetSigFromSignedBlob(void *buf)
{
return (buf ? ((u8*)buf + 4) : NULL);
}
NX_INLINE u64 signatureGetBlockSizeFromSignedBlob(void *buf, bool big_endian_sig_type)
{
return signatureGetBlockSizeByType(signatureGetTypeFromSignedBlob(buf, big_endian_sig_type));
}
NX_INLINE void *signatureGetPayloadFromSignedBlob(void *buf, bool big_endian_sig_type)
{
if (!buf) return NULL;
u32 sig_type = signatureGetTypeFromSignedBlob(buf, big_endian_sig_type);
return (signatureIsValidType(sig_type) ? (void*)((u8*)buf + signatureGetBlockSizeByType(sig_type)) : NULL);
}
#ifdef __cplusplus
}
#endif
#endif /* __SIGNATURE_H__ */

81
include/core/smc.h Normal file
View File

@ -0,0 +1,81 @@
/*
* smc.h
*
* Copyright (c) Atmosphère-NX.
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __SMC_H__
#define __SMC_H__
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
SmcKeyType_Default = 0, ///< Also known as "aes_kek_generation_source".
SmcKeyType_NormalOnly = 1,
SmcKeyType_RecoveryOnly = 2,
SmcKeyType_NormalAndRecovery = 3,
SmcKeyType_Count = 4 ///< Total values supported by this enum.
} SmcKeyType;
typedef enum {
SmcSealKey_LoadAesKey = 0,
SmcSealKey_DecryptDeviceUniqueData = 1,
SmcSealKey_ImportLotusKey = 2,
SmcSealKey_ImportEsDeviceKey = 3,
SmcSealKey_ReencryptDeviceUniqueData = 4,
SmcSealKey_ImportSslKey = 5,
SmcSealKey_ImportEsClientCertKey = 6,
SmcSealKey_Count = 7 ///< Total values supported by this enum.
} SmcSealKey;
typedef struct {
union {
u32 value; ///< Can be used with spl calls.
struct {
u32 is_device_unique : 1;
u32 key_type_idx : 4; ///< SmcKeyType.
u32 seal_key_idx : 3; ///< SmcSealKey.
u32 reserved : 24;
} fields;
};
} SmcGenerateAesKekOption;
NXDT_ASSERT(SmcGenerateAesKekOption, 0x4);
/// Helper inline functions.
NX_INLINE void smcPrepareGenerateAesKekOption(bool is_device_unique, u32 key_type_idx, u32 seal_key_idx, SmcGenerateAesKekOption *out)
{
if (key_type_idx >= SmcKeyType_Count || seal_key_idx >= SmcSealKey_Count) return;
out->fields.is_device_unique = (u32)(is_device_unique & 1);
out->fields.key_type_idx = key_type_idx;
out->fields.seal_key_idx = seal_key_idx;
out->fields.reserved = 0;
}
#ifdef __cplusplus
}
#endif
#endif /* __SMC_H__ */

265
include/core/tik.h Normal file
View File

@ -0,0 +1,265 @@
/*
* tik.h
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __TIK_H__
#define __TIK_H__
#include "signature.h"
#ifdef __cplusplus
extern "C" {
#endif
#define SIGNED_TIK_MIN_SIZE sizeof(TikSigHmac160) /* Assuming no ESV1/ESV2 records are available. */
#define SIGNED_TIK_MAX_SIZE 0x400 /* Max ticket entry size in the ES ticket system savedata file. */
#define TIK_FORMAT_VERSION 2
#define GENERATE_TIK_STRUCT(sigtype, tiksize) \
typedef struct { \
SignatureBlock##sigtype sig_block; \
TikCommonBlock tik_common_block; \
u8 es_section_record_data[]; \
} TikSig##sigtype; \
NXDT_ASSERT(TikSig##sigtype, tiksize);
typedef enum {
TikTitleKeyType_Common = 0,
TikTitleKeyType_Personalized = 1,
TikTitleKeyType_Count = 2 ///< Total values supported by this enum.
} TikTitleKeyType;
typedef enum {
TikLicenseType_Permanent = 0,
TikLicenseType_Demo = 1,
TikLicenseType_Trial = 2,
TikLicenseType_Rental = 3,
TikLicenseType_Subscription = 4,
TikLicenseType_Service = 5,
TikLicenseType_Count = 6 ///< Total values supported by this enum.
} TikLicenseType;
typedef enum {
TikPropertyMask_None = 0,
TikPropertyMask_PreInstallation = BIT(0), ///< Determines if the title comes pre-installed on the device. Most likely unused -- a remnant from previous ticket formats.
TikPropertyMask_SharedTitle = BIT(1), ///< Determines if the title holds shared contents only. Most likely unused -- a remnant from previous ticket formats.
TikPropertyMask_AllContents = BIT(2), ///< Determines if the content index mask shall be bypassed. Most likely unused -- a remnant from previous ticket formats.
TikPropertyMask_DeviceLinkIndepedent = BIT(3), ///< Determines if the console should *not* connect to the Internet to verify if the title's being used by the primary console.
TikPropertyMask_Volatile = BIT(4), ///< Determines if the ticket copy inside ticket.bin should be encrypted or not.
TikPropertyMask_ELicenseRequired = BIT(5), ///< Determines if the console should connect to the Internet to perform license verification.
TikPropertyMask_Count = 6 ///< Total values supported by this enum.
} TikPropertyMask;
/// Placed after the ticket signature block.
typedef struct {
char issuer[0x40];
u8 titlekey_block[0x100];
u8 format_version; ///< Always matches TIK_FORMAT_VERSION.
u8 titlekey_type; ///< TikTitleKeyType.
u16 ticket_version;
u8 license_type; ///< TikLicenseType.
u8 key_generation; ///< NcaKeyGeneration.
u16 property_mask; ///< TikPropertyMask.
u8 reserved[0x8];
u64 ticket_id;
u64 device_id;
FsRightsId rights_id;
u32 account_id;
u32 sect_total_size;
u32 sect_hdr_offset;
u16 sect_hdr_count;
u16 sect_hdr_entry_size;
} TikCommonBlock;
NXDT_ASSERT(TikCommonBlock, 0x180);
/// ESV1/ESV2 section records are placed right after the ticket data. These aren't available in TikTitleKeyType_Common tickets.
/// These are only used if the sect_* fields from the common block are non-zero (other than 'sect_hdr_offset').
/// Each ESV2 section record is followed by a 'record_count' number of ESV1 records, each one of 'record_size' size.
typedef enum {
TikSectionType_None = 0,
TikSectionType_Permanent = 1,
TikSectionType_Subscription = 2,
TikSectionType_Content = 3,
TikSectionType_ContentConsumption = 4,
TikSectionType_AccessTitle = 5,
TikSectionType_LimitedResource = 6,
TikSectionType_Count = 7 ///< Total values supported by this enum.
} TikSectionType;
typedef struct {
u32 sect_offset;
u32 record_size;
u32 section_size;
u16 record_count;
u16 section_type; ///< TikSectionType.
} TikESV2SectionRecord;
/// Used with TikSectionType_Permanent.
typedef struct {
u8 ref_id[0x10];
u32 ref_id_attr;
} TikESV1PermanentRecord;
/// Used with TikSectionType_Subscription.
typedef struct {
u32 limit;
u8 ref_id[0x10];
u32 ref_id_attr;
} TikESV1SubscriptionRecord;
/// Used with TikSectionType_Content.
typedef struct {
u32 offset;
u8 access_mask[0x80];
} TikESV1ContentRecord;
/// Used with TikSectionType_ContentConsumption.
typedef struct {
u16 index;
u16 code;
u32 limit;
} TikESV1ContentConsumptionRecord;
/// Used with TikSectionType_AccessTitle.
typedef struct {
u64 access_title_id;
u64 access_title_mask;
} TikESV1AccessTitleRecord;
/// Used with TikSectionType_LimitedResource.
typedef struct {
u32 limit;
u8 ref_id[0x10];
u32 ref_id_attr;
} TikESV1LimitedResourceRecord;
/// All tickets generated below use a little endian sig_type field.
GENERATE_TIK_STRUCT(Rsa4096, 0x3C0); /// RSA-4096 signature.
GENERATE_TIK_STRUCT(Rsa2048, 0x2C0); /// RSA-2048 signature.
GENERATE_TIK_STRUCT(Ecc480, 0x200); /// ECC signature.
GENERATE_TIK_STRUCT(Hmac160, 0x1C0); /// HMAC signature.
/// Ticket type.
typedef enum {
TikType_None = 0,
TikType_SigRsa4096 = 1,
TikType_SigRsa2048 = 2,
TikType_SigEcc480 = 3,
TikType_SigHmac160 = 4,
TikType_Count = 5 ///< Total values supported by this enum.
} TikType;
/// Used to store ticket type, size and raw data, as well as titlekey data.
typedef struct {
u8 type; ///< TikType.
u64 size; ///< Raw ticket size.
u8 data[SIGNED_TIK_MAX_SIZE]; ///< Raw ticket data.
u8 key_generation; ///< NcaKeyGeneration.
u8 enc_titlekey[0x10]; ///< Titlekey with titlekek crypto (RSA-OAEP unwrapped if dealing with a TikTitleKeyType_Personalized ticket).
char enc_titlekey_str[0x21]; ///< Character string representation of enc_titlekey.
u8 dec_titlekey[0x10]; ///< Titlekey without titlekek crypto. Ready to use for NCA FS section decryption.
char dec_titlekey_str[0x21]; ///< Character string representation of dec_titlekey.
char rights_id_str[0x21]; ///< Character string representation of the rights ID from the ticket.
} Ticket;
/// Retrieves a ticket from either the ES ticket system savedata file (eMMC BIS System partition) or the secure Hash FS partition from an inserted gamecard.
/// Both the input rights ID and key generation values must have been retrieved from a NCA that depends on the desired ticket.
/// Titlekey is also RSA-OAEP unwrapped (if needed) and titlekek-decrypted right away.
bool tikRetrieveTicketByRightsId(Ticket *dst, const FsRightsId *id, u8 key_generation, bool use_gamecard);
/// Converts a TikTitleKeyType_Personalized ticket into a TikTitleKeyType_Common ticket and optionally generates a raw certificate chain for the new signature issuer.
/// Bear in mind the 'size' member from the Ticket parameter will be updated by this function to remove any possible references to ESV1/ESV2 records.
/// If both 'out_raw_cert_chain' and 'out_raw_cert_chain_size' pointers are provided, raw certificate chain data will be saved to them.
/// certGenerateRawCertificateChainBySignatureIssuer() is used internally, so the output buffer must be freed by the user.
bool tikConvertPersonalizedTicketToCommonTicket(Ticket *tik, u8 **out_raw_cert_chain, u64 *out_raw_cert_chain_size);
/// Helper inline functions for signed ticket blobs.
NX_INLINE TikCommonBlock *tikGetCommonBlockFromSignedTicketBlob(void *buf)
{
return (TikCommonBlock*)signatureGetPayloadFromSignedBlob(buf, false);
}
NX_INLINE u64 tikGetSectionRecordsSizeFromSignedTicketBlob(void *buf)
{
TikCommonBlock *tik_common_block = tikGetCommonBlockFromSignedTicketBlob(buf);
if (!tik_common_block) return 0;
u64 offset = sizeof(TikCommonBlock), out_size = 0;
for(u32 i = 0; i < tik_common_block->sect_hdr_count; i++)
{
TikESV2SectionRecord *rec = (TikESV2SectionRecord*)((u8*)tik_common_block + offset);
offset += (sizeof(TikESV2SectionRecord) + ((u64)rec->record_count * (u64)rec->record_size));
out_size += offset;
}
return out_size;
}
NX_INLINE bool tikIsValidSignedTicketBlob(void *buf)
{
u64 ticket_size = (signatureGetBlockSizeFromSignedBlob(buf, false) + sizeof(TikCommonBlock));
return (ticket_size > sizeof(TikCommonBlock) && (ticket_size + tikGetSectionRecordsSizeFromSignedTicketBlob(buf)) <= SIGNED_TIK_MAX_SIZE);
}
NX_INLINE u64 tikGetSignedTicketBlobSize(void *buf)
{
return (tikIsValidSignedTicketBlob(buf) ? (signatureGetBlockSizeFromSignedBlob(buf, false) + sizeof(TikCommonBlock) + tikGetSectionRecordsSizeFromSignedTicketBlob(buf)) : 0);
}
NX_INLINE u64 tikGetSignedTicketBlobHashAreaSize(void *buf)
{
return (tikIsValidSignedTicketBlob(buf) ? (sizeof(TikCommonBlock) + tikGetSectionRecordsSizeFromSignedTicketBlob(buf)) : 0);
}
/// Helper inline functions for Ticket elements.
NX_INLINE bool tikIsValidTicket(Ticket *tik)
{
return (tik && tik->type > TikType_None && tik->type < TikType_Count && tik->size >= SIGNED_TIK_MIN_SIZE && tik->size <= SIGNED_TIK_MAX_SIZE && tikIsValidSignedTicketBlob(tik->data));
}
NX_INLINE TikCommonBlock *tikGetCommonBlockFromTicket(Ticket *tik)
{
return (tikIsValidTicket(tik) ? tikGetCommonBlockFromSignedTicketBlob(tik->data) : NULL);
}
NX_INLINE bool tikIsPersonalizedTicket(Ticket *tik)
{
TikCommonBlock *tik_common_block = tikGetCommonBlockFromTicket(tik);
return (tik_common_block ? (tik_common_block->titlekey_type == TikTitleKeyType_Personalized) : false);
}
NX_INLINE u64 tikGetHashAreaSizeFromTicket(Ticket *tik)
{
return (tikIsValidTicket(tik) ? tikGetSignedTicketBlobHashAreaSize(tik->data) : 0);
}
#ifdef __cplusplus
}
#endif
#endif /* __TIK_H__ */

349
include/core/title.h Normal file
View File

@ -0,0 +1,349 @@
/*
* title.h
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __TITLE_H__
#define __TITLE_H__
#ifdef __cplusplus
extern "C" {
#endif
#define TITLE_PATCH_ID_OFFSET (u64)0x800
#define TITLE_ADDONCONTENT_ID_OFFSET (u64)0x1000
#define TITLE_ADDONCONTENT_CONVERSION_MASK (u64)0xFFFFFFFFFFFFF000
#define TITLE_ADDONCONTENT_MIN_INDEX 1
#define TITLE_ADDONCONTENT_MAX_INDEX 2000
#define TITLE_DELTA_ID_OFFSET (u64)0xC00
/// Generated using ns application records and/or ncm content meta keys.
/// Used by the UI to display title lists.
typedef struct {
u64 title_id; ///< Title ID from the application / system title this data belongs to.
NacpLanguageEntry lang_entry; ///< UTF-8 strings in the console language.
u32 icon_size; ///< JPEG icon size.
u8 *icon; ///< JPEG icon data.
} TitleApplicationMetadata;
/// Used to display gamecard-specific title information.
typedef struct {
TitleApplicationMetadata *app_metadata; ///< User application metadata.
bool has_patch; ///< Set to true if a patch is also available in the inserted gamecard for this user application.
Version version; ///< Reflects the title version stored in the inserted gamecard, either from a base application or a patch.
char display_version[32]; ///< Reflects the title display version from the NACP belonging to either a base application or a patch.
u32 dlc_count; ///< Reflects the number of DLCs available for this application in the inserted gamecard.
} TitleGameCardApplicationMetadata;
/// Generated using ncm calls.
/// User applications: the previous/next pointers reference other user applications with the same ID.
/// Patches: the previous/next pointers reference other patches with the same ID.
/// Add-on contents: the previous/next pointers reference sibling add-on contents.
/// Add-on content patches: the previous/next pointers reference other patches with the same ID and/or other patches for sibling add-on contents.
typedef struct _TitleInfo {
u8 storage_id; ///< NcmStorageId.
NcmContentMetaKey meta_key; ///< Used with ncm calls.
Version version; ///< Holds the same value from meta_key.version.
u32 content_count; ///< Content info count.
NcmContentInfo *content_infos; ///< Content info entries from this title.
u64 size; ///< Total title size.
char size_str[32]; ///< Total title size string.
TitleApplicationMetadata *app_metadata; ///< User application metadata.
struct _TitleInfo *previous, *next; ///< Linked lists.
} TitleInfo;
/// Used to deal with user applications stored in the eMMC, SD card and/or gamecard.
/// The previous and next pointers from the TitleInfo elements are used to traverse through multiple user applications, patches, add-on contents and or add-on content patches.
typedef struct {
TitleInfo *app_info; ///< Pointer to a TitleInfo element for the first detected user application entry matching the provided application ID.
TitleInfo *patch_info; ///< Pointer to a TitleInfo element for the first detected patch entry matching the provided application ID.
TitleInfo *aoc_info; ///< Pointer to a TitleInfo element for the first detected add-on content entry matching the provided application ID.
TitleInfo *aoc_patch_info; ///< Pointer to a TitleInfo element for the first detected add-on content patch entry matching the provided application ID.
} TitleUserApplicationData;
typedef enum {
TitleNamingConvention_Full = 0, ///< Individual titles: "{Name} [{Id}][v{Version}][{Type}]".
///< Gamecards: "{Name1} [{Id1}][v{Version1}] + ... + {NameN} [{IdN}][v{VersionN}]".
TitleNamingConvention_IdAndVersionOnly = 1, ///< Individual titles: "{Id}_v{Version}_{Type}".
///< Gamecards: "{TitleId1}_v{TitleVersion1}_{TitleType1} + ... + {TitleIdN}_v{TitleVersionN}_{TitleTypeN}".
TitleNamingConvention_Count = 2 ///< Total values supported by this enum.
} TitleNamingConvention;
typedef enum {
TitleFileNameIllegalCharReplaceType_None = 0,
TitleFileNameIllegalCharReplaceType_IllegalFsChars = 1,
TitleFileNameIllegalCharReplaceType_KeepAsciiCharsOnly = 2,
TitleFileNameIllegalCharReplaceType_Count = 3 ///< Total values supported by this enum.
} TitleFileNameIllegalCharReplaceType;
/// Initializes the title interface.
bool titleInitialize(void);
/// Closes the title interface.
void titleExit(void);
/// Returns a pointer to a ncm database handle using a NcmStorageId value.
NcmContentMetaDatabase *titleGetNcmDatabaseByStorageId(u8 storage_id);
/// Returns a pointer to a ncm storage handle using a NcmStorageId value.
NcmContentStorage *titleGetNcmStorageByStorageId(u8 storage_id);
/// Returns a pointer to a dynamically allocated array of pointers to TitleApplicationMetadata entries, as well as their count. Returns NULL if an error occurs.
/// If 'is_system' is true, TitleApplicationMetadata entries from available system titles (NcmStorageId_BuiltInSystem) will be returned.
/// Otherwise, TitleApplicationMetadata entries from user applications with available content data (NcmStorageId_BuiltInUser, NcmStorageId_SdCard, NcmStorageId_GameCard) will be returned.
/// The allocated buffer must be freed by the caller using free().
TitleApplicationMetadata **titleGetApplicationMetadataEntries(bool is_system, u32 *out_count);
/// Returns a pointer to a dynamically allocated array of TitleGameCardApplicationMetadata elements generated from gamecard user titles, as well as their count.
/// Returns NULL if an error occurs.
/// The allocated buffer must be freed by the caller using free().
TitleGameCardApplicationMetadata *titleGetGameCardApplicationMetadataEntries(u32 *out_count);
/// Returns a pointer to a dynamically allocated TitleInfo element with a matching storage ID and title ID. Returns NULL if an error occurs.
/// If NcmStorageId_Any is used, the first entry with a matching title ID is returned.
/// Use titleFreeTitleInfo() to free the returned data.
TitleInfo *titleGetInfoFromStorageByTitleId(u8 storage_id, u64 title_id);
/// Frees a dynamically allocated TitleInfo element.
void titleFreeTitleInfo(TitleInfo **info);
/// Populates a TitleUserApplicationData element with dynamically allocated data using a user application ID.
/// Use titleFreeUserApplicationData() to free the populated data.
bool titleGetUserApplicationData(u64 app_id, TitleUserApplicationData *out);
/// Frees data populated by titleGetUserApplicationData().
void titleFreeUserApplicationData(TitleUserApplicationData *user_app_data);
/// Takes an input TitleInfo object with meta type NcmContentMetaType_AddOnContent or NcmContentMetaType_DataPatch.
/// Returns a linked list of TitleInfo elements with title IDs matching the corresponding base/patch title ID, depending on the meta type of the input TitleInfo object.
/// Particularly useful to display add-on-content base/patch titles related to a specific add-on-content (patch) entry.
/// Use titleFreeTitleInfo() to free the returned data.
TitleInfo *titleGetAddOnContentBaseOrPatchList(TitleInfo *title_info);
/// Returns true if orphan titles are available.
/// Orphan titles are patches or add-on contents with no NsApplicationControlData available for their parent user application ID.
bool titleAreOrphanTitlesAvailable(void);
/// Returns a pointer to a dynamically allocated array of orphan TitleInfo entries, as well as their count. Returns NULL if an error occurs.
/// Use titleFreeOrphanTitles() to free the returned data.
TitleInfo **titleGetOrphanTitles(u32 *out_count);
/// Frees orphan title info data returned by titleGetInfoFromOrphanTitles().
void titleFreeOrphanTitles(TitleInfo ***orphan_info);
/// Checks if a gamecard status update has been detected by the background gamecard title info thread (e.g. after a new gamecard has been inserted, of after the current one has been taken out).
/// If this function returns true and functions such as titleGetInfoFromStorageByTitleId(), titleGetUserApplicationData() or titleGetInfoFromOrphanTitles() have been previously called:
/// 1. Their returned data must be freed.
/// 2. They must be called again.
bool titleIsGameCardInfoUpdated(void);
/// Returns a pointer to a dynamically allocated buffer that holds a filename string suitable for output title dumps. Returns NULL if an error occurs.
char *titleGenerateFileName(TitleInfo *title_info, u8 naming_convention, u8 illegal_char_replace_type);
/// Returns a pointer to a dynamically allocated buffer that holds a filename string suitable for output gamecard dumps. Returns NULL if an error occurs.
/// A valid gamecard must be inserted, and title info must have been loaded from it accordingly.
char *titleGenerateGameCardFileName(u8 naming_convention, u8 illegal_char_replace_type);
/// Returns a pointer to a string holding a user-friendly name for the provided NcmStorageId value. Returns NULL if the provided value is invalid.
const char *titleGetNcmStorageIdName(u8 storage_id);
/// Returns a pointer to a string holding the name of the provided NcmContentType value. Returns NULL if the provided value is invalid.
const char *titleGetNcmContentTypeName(u8 content_type);
/// Returns a pointer to a string holding the name of the provided NcmContentMetaType value. Returns NULL if the provided value is invalid.
const char *titleGetNcmContentMetaTypeName(u8 content_meta_type);
/// Miscellaneous functions.
NX_INLINE bool titleIsValidInfoBlock(TitleInfo *title_info)
{
return (title_info && title_info->storage_id >= NcmStorageId_GameCard && title_info->storage_id <= NcmStorageId_SdCard && title_info->meta_key.id && \
((title_info->meta_key.type >= NcmContentMetaType_SystemProgram && title_info->meta_key.type <= NcmContentMetaType_BootImagePackageSafe) || \
(title_info->meta_key.type >= NcmContentMetaType_Application && title_info->meta_key.type <= NcmContentMetaType_DataPatch)) && \
title_info->content_count && title_info->content_infos);
}
NX_INLINE u64 titleGetPatchIdByApplicationId(u64 app_id)
{
return (app_id + TITLE_PATCH_ID_OFFSET);
}
NX_INLINE u64 titleGetApplicationIdByPatchId(u64 patch_id)
{
return (patch_id - TITLE_PATCH_ID_OFFSET);
}
NX_INLINE bool titleCheckIfPatchIdBelongsToApplicationId(u64 app_id, u64 patch_id)
{
return (patch_id == titleGetPatchIdByApplicationId(app_id));
}
NX_INLINE u64 titleGetAddOnContentBaseIdByApplicationId(u64 app_id)
{
return ((app_id & TITLE_ADDONCONTENT_CONVERSION_MASK) + TITLE_ADDONCONTENT_ID_OFFSET);
}
NX_INLINE u64 titleGetAddOnContentMinIdByBaseId(u64 aoc_base_id)
{
return (aoc_base_id + TITLE_ADDONCONTENT_MIN_INDEX);
}
NX_INLINE u64 titleGetAddOnContentMaxIdByBaseId(u64 aoc_base_id)
{
return (aoc_base_id + TITLE_ADDONCONTENT_MAX_INDEX);
}
NX_INLINE u64 titleGetApplicationIdByAddOnContentId(u64 aoc_id)
{
return ((aoc_id - TITLE_ADDONCONTENT_ID_OFFSET) & TITLE_ADDONCONTENT_CONVERSION_MASK);
}
NX_INLINE bool titleIsAddOnContentIdValid(u64 aoc_id, u64 aoc_min_id, u64 aoc_max_id)
{
return (aoc_min_id <= aoc_id && aoc_id <= aoc_max_id);
}
NX_INLINE bool titleCheckIfAddOnContentIdBelongsToApplicationId(u64 app_id, u64 aoc_id)
{
u64 aoc_base_id = titleGetAddOnContentBaseIdByApplicationId(app_id);
u64 aoc_min_id = titleGetAddOnContentMinIdByBaseId(aoc_base_id);
u64 aoc_max_id = titleGetAddOnContentMaxIdByBaseId(aoc_base_id);
return titleIsAddOnContentIdValid(aoc_id, aoc_min_id, aoc_max_id);
}
NX_INLINE bool titleCheckIfAddOnContentIdsAreSiblings(u64 aoc_id_1, u64 aoc_id_2)
{
u64 app_id_1 = titleGetApplicationIdByAddOnContentId(aoc_id_1);
u64 app_id_2 = titleGetApplicationIdByAddOnContentId(aoc_id_2);
return (app_id_1 == app_id_2 && titleCheckIfAddOnContentIdBelongsToApplicationId(app_id_1, aoc_id_1) && titleCheckIfAddOnContentIdBelongsToApplicationId(app_id_2, aoc_id_2));
}
/// Nintendo uses one-based indexes for IDs... but we won't.
NX_INLINE u64 titleGetAddOnContentIdByApplicationIdAndIndex(u64 app_id, u16 idx)
{
return (titleGetAddOnContentBaseIdByApplicationId(app_id) + 1 + idx);
}
NX_INLINE u64 titleGetDeltaIdByApplicationId(u64 app_id)
{
return (app_id + TITLE_DELTA_ID_OFFSET);
}
NX_INLINE u64 titleGetApplicationIdByDeltaId(u64 delta_id)
{
return (delta_id - TITLE_DELTA_ID_OFFSET);
}
NX_INLINE bool titleCheckIfDeltaIdBelongsToApplicationId(u64 app_id, u64 delta_id)
{
return (delta_id == titleGetDeltaIdByApplicationId(app_id));
}
NX_INLINE u64 titleGetDataPatchIdByAddOnContentId(u64 aoc_id)
{
return (aoc_id + TITLE_PATCH_ID_OFFSET);
}
NX_INLINE u64 titleGetAddOnContentIdByDataPatchId(u64 data_patch_id)
{
return (data_patch_id - TITLE_PATCH_ID_OFFSET);
}
NX_INLINE bool titleCheckIfDataPatchIdBelongsToAddOnContentId(u64 aoc_id, u64 data_patch_id)
{
return (data_patch_id == titleGetDataPatchIdByAddOnContentId(aoc_id));
}
NX_INLINE u64 titleGetApplicationIdByDataPatchId(u64 data_patch_id)
{
return titleGetApplicationIdByAddOnContentId(titleGetAddOnContentIdByDataPatchId(data_patch_id));
}
NX_INLINE bool titleCheckIfDataPatchIdBelongsToApplicationId(u64 app_id, u64 data_patch_id)
{
return titleCheckIfAddOnContentIdBelongsToApplicationId(app_id, titleGetAddOnContentIdByDataPatchId(data_patch_id));
}
NX_INLINE bool titleCheckIfDataPatchIdsAreSiblings(u64 data_patch_id_1, u64 data_patch_id_2)
{
u64 app_id_1 = titleGetApplicationIdByDataPatchId(data_patch_id_1);
u64 app_id_2 = titleGetApplicationIdByDataPatchId(data_patch_id_2);
return (app_id_1 == app_id_2 && titleCheckIfDataPatchIdBelongsToApplicationId(app_id_1, data_patch_id_1) && titleCheckIfDataPatchIdBelongsToApplicationId(app_id_2, data_patch_id_2));
}
NX_INLINE u32 titleGetContentCountByType(TitleInfo *info, u8 content_type)
{
if (!info || !info->content_count || !info->content_infos || content_type > NcmContentType_DeltaFragment) return 0;
u32 cnt = 0;
for(u32 i = 0; i < info->content_count; i++)
{
if (info->content_infos[i].content_type == content_type) cnt++;
}
return cnt;
}
NX_INLINE NcmContentInfo *titleGetContentInfoByTypeAndIdOffset(TitleInfo *info, u8 content_type, u8 id_offset)
{
if (!info || !info->content_count || !info->content_infos || content_type > NcmContentType_DeltaFragment) return NULL;
for(u32 i = 0; i < info->content_count; i++)
{
NcmContentInfo *cur_content_info = &(info->content_infos[i]);
if (cur_content_info->content_type == content_type && cur_content_info->id_offset == id_offset) return cur_content_info;
}
return NULL;
}
NX_INLINE u32 titleGetCountFromInfoBlock(TitleInfo *title_info)
{
if (!title_info) return 0;
u32 count = 1;
TitleInfo *cur_info = title_info->previous;
while(cur_info)
{
count++;
cur_info = cur_info->previous;
}
cur_info = title_info->next;
while(cur_info)
{
count++;
cur_info = cur_info->next;
}
return count;
}
#ifdef __cplusplus
}
#endif
#endif /* __TITLE_H__ */

54
include/core/ums.h Normal file
View File

@ -0,0 +1,54 @@
/*
* ums.h
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __UMS_H__
#define __UMS_H__
#include <usbhsfs.h>
#ifdef __cplusplus
extern "C" {
#endif
/// Initializes the USB Mass Storage interface.
bool umsInitialize(void);
/// Closes the USB Mass Storage interface.
void umsExit(void);
/// Returns true if USB Mass Storage device info has been updated.
bool umsIsDeviceInfoUpdated(void);
/// Returns a pointer to a dynamically allocated array of UsbHsFsDevice elements. The allocated buffer must be freed by the caller.
/// Returns NULL if an error occurs.
UsbHsFsDevice *umsGetDevices(u32 *out_count);
/// Unmounts a USB Mass Storage device using a UsbHsFsDevice element.
/// If successful, USB Mass Storage device info will be automatically reloaded, and the next call to umsIsDeviceInfoUpdated() shall return true.
bool umsUnmountDevice(const UsbHsFsDevice *device);
#ifdef __cplusplus
}
#endif
#endif /* __UMS_H__ */

95
include/core/usb.h Normal file
View File

@ -0,0 +1,95 @@
/*
* usb.h
*
* Heavily based in usb_comms from libnx.
*
* Copyright (c) 2018-2020, Switchbrew and libnx contributors.
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __USB_H__
#define __USB_H__
#ifdef __cplusplus
extern "C" {
#endif
#define USB_TRANSFER_BUFFER_SIZE 0x800000 /* 8 MiB. */
/// Used to indicate the USB speed selected by the host device.
typedef enum {
UsbHostSpeed_None = 0,
UsbHostSpeed_FullSpeed = 1, ///< USB 1.x.
UsbHostSpeed_HighSpeed = 2, ///< USB 2.0.
UsbHostSpeed_SuperSpeed = 3, ///< USB 3.0.
UsbHostSpeed_Count = 4 ///< Total values supported by this enum.
} UsbHostSpeed;
/// Initializes the USB interface, input and output endpoints and allocates an internal transfer buffer.
bool usbInitialize(void);
/// Closes the USB interface, input and output endpoints and frees the transfer buffer.
void usbExit(void);
/// Returns a pointer to a dynamically allocated, page aligned memory buffer that's suitable for USB transfers.
void *usbAllocatePageAlignedBuffer(size_t size);
/// Used to check if the console has been connected to a USB host device and if a valid USB session has been established.
/// Returns a value from the UsbHostSpeed enum.
u8 usbIsReady(void);
/// Sends file properties to the host device before starting a file data transfer. If needed, it must be called before usbSendFileData().
/// 'file_size' may be zero if an empty file shall be created, in which case no file data transfer will be necessary.
/// Calling this function before finishing an ongoing file data transfer will result in an error.
/// Under NSP transfer mode, this function must be called right before transferring data from each NSP file entry to the host device, which should in turn write it all to the same output file.
bool usbSendFileProperties(u64 file_size, const char *filename);
/// Sends NSP properties to the host device and enables NSP transfer mode. If needed, it must be called before usbSendFileData().
/// Both 'nsp_size' and 'nsp_header_size' must be greater than zero. 'nsp_size' must also be greater than 'nsp_header_size'.
/// Calling this function after NSP transfer mode has already been enabled will result in an error.
/// The host device should immediately write 'nsp_header_size' padding at the start of the output file and start listening for further usbSendFileProperties() calls, or a usbSendNspHeader() call.
bool usbSendNspProperties(u64 nsp_size, const char *filename, u32 nsp_header_size);
/// Performs a file data transfer. Must be continuously called after usbSendFileProperties() / usbSendNspProperties() until all file data has been transferred.
/// Data chunk size must not exceed USB_TRANSFER_BUFFER_SIZE.
/// If the last file data chunk is aligned to the endpoint max packet size, the host device should expect a Zero Length Termination (ZLT) packet.
/// Calling this function if there's no remaining data to transfer will result in an error.
bool usbSendFileData(const void *data, u64 data_size);
/// Used to gracefully cancel an ongoing file transfer. The current USB session is kept alive.
void usbCancelFileTransfer(void);
/// Sends NSP header data to the host device, making it rewind the NSP file pointer to write this data, essentially finishing the NSP transfer process.
/// Must be called after the data from all NSP file entries has been transferred using both usbSendNspProperties() and usbSendFileData() calls.
/// If the NSP header size is aligned to the endpoint max packet size, the host device should expect a Zero Length Termination (ZLT) packet.
bool usbSendNspHeader(const void *nsp_header, u32 nsp_header_size);
/// Informs the host device that an extracted filesystem dump (e.g. HFS, PFS, RomFS) is about to begin.
bool usbStartExtractedFsDump(u64 extracted_fs_size, const char *extracted_fs_root_path);
/// Informs the host device that a previously started filesystem dump (via usbStartExtractedFsDump()) has finished.
/// This is only issued after all extracted file entries have been successfully transferred to the host device.
void usbEndExtractedFsDump(void);
#ifdef __cplusplus
}
#endif
#endif /* __USB_H__ */

143
include/defines.h Normal file
View File

@ -0,0 +1,143 @@
/*
* defines.h
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __DEFINES_H__
#define __DEFINES_H__
/* Broadly useful language defines. */
#define MEMBER_SIZE(type, member) sizeof(((type*)NULL)->member)
#define MAX_ELEMENTS(x) ((sizeof((x))) / (sizeof((x)[0])))
#define ALIGN_UP(x, y) (((x) + ((y) - 1)) & ~((y) - 1))
#define ALIGN_DOWN(x, y) ((x) & ~((y) - 1))
#define IS_ALIGNED(x, y) (((x) & ((y) - 1)) == 0)
#define IS_POWER_OF_TWO(x) ((x) > 0 && ((x) & ((x) - 1)) == 0)
#define DIVIDE_UP(x, y) (((x) + ((y) - 1)) / (y))
#define CONCATENATE_IMPL(s1, s2) s1##s2
#define CONCATENATE(s1, s2) CONCATENATE_IMPL(s1, s2)
#define ANONYMOUS_VARIABLE(pref) CONCATENATE(pref, __LINE__)
#define NON_COPYABLE(cls) \
cls(const cls&) = delete; \
cls& operator=(const cls&) = delete
#define NON_MOVEABLE(cls) \
cls(cls&&) = delete; \
cls& operator=(cls&&) = delete
#define ALWAYS_INLINE inline __attribute__((always_inline))
#define ALWAYS_INLINE_LAMBDA __attribute__((always_inline))
#define CLEANUP(func) __attribute__((__cleanup__(func)))
#define NXDT_ASSERT(name, size) static_assert(sizeof(name) == (size), "Bad size for " #name "! Expected " #size ".")
/* Global constants used throughout the application. */
#define SHA256_HASH_STR_SIZE ((SHA256_HASH_SIZE * 2) + 1) /* Includes NULL terminator. */
#define THIRTY_FPS_DELAY (u64)33333333 /* 1 / 30 = 33.33 milliseconds. */
#define FS_SYSMODULE_TID (u64)0x0100000000000000
#define BOOT_SYSMODULE_TID (u64)0x0100000000000005
#define SPL_SYSMODULE_TID (u64)0x0100000000000028
#define ES_SYSMODULE_TID (u64)0x0100000000000033
#define SYSTEM_UPDATE_TID (u64)0x0100000000000816
#define QLAUNCH_TID (u64)0x0100000000001000
#define FAT32_FILESIZE_LIMIT (u64)UINT32_MAX /* 4 GiB - 1 (4294967295 bytes). */
#define CONCATENATION_FILE_PART_SIZE (u64)0xFFFF0000 /* 4 GiB - 65536 (4294901760 bytes). */
#define UTF8_BOM "\xEF\xBB\xBF"
#define CRLF "\r\n"
#define DEVOPTAB_SDMC_DEVICE "sdmc:"
#define HBMENU_BASE_PATH "/switch/"
#define APP_BASE_PATH HBMENU_BASE_PATH APP_TITLE "/"
#define GAMECARD_SUBDIR "Gamecard"
#define HFS_SUBDIR "HFS"
#define NSP_SUBDIR "NSP"
#define TICKET_SUBDIR "Ticket"
#define NCA_SUBDIR "NCA"
#define NCA_FS_SUBDIR "NCA FS"
#define CONFIG_FILE_NAME APP_TITLE "_config.json"
#define DEFAULT_CONFIG_PATH "romfs:/default_config.json"
#define NRO_NAME APP_TITLE ".nro"
#define NRO_PATH DEVOPTAB_SDMC_DEVICE APP_BASE_PATH NRO_NAME
#define NRO_TMP_PATH NRO_PATH ".tmp"
#define PROD_KEYS_FILE_PATH DEVOPTAB_SDMC_DEVICE HBMENU_BASE_PATH "prod.keys" /* Retail unit keys. */
#define DEV_KEYS_FILE_PATH DEVOPTAB_SDMC_DEVICE HBMENU_BASE_PATH "dev.keys" /* Development unit keys. */
#define LOG_FILE_NAME APP_TITLE ".log"
#define LOG_BUF_SIZE 0x400000 /* 4 MiB. */
#define LOG_FORCE_FLUSH 0 /* Forces a log buffer flush each time the logfile is written to. */
#define BIS_SYSTEM_PARTITION_MOUNT_NAME "sys:"
/// Reference: https://docs.microsoft.com/en-us/windows/win32/fileio/filesystem-functionality-comparison#limits.
/// Reference: https://en.wikipedia.org/wiki/Comparison_of_file_systems#Limits.
/// Most modern filesystems use a 255-byte limit instead of 255-character/codepoint limit, so that's what we're gonna use.
#define FS_MAX_FILENAME_LENGTH 255
#define SDMC_MAX_FILENAME_LENGTH 128 /* Arbitrarily set, I'm tired of FS sysmodule shenanigans. */
#define REPEATING_TASK_INTERVAL 250 /* 250 milliseconds. */
#define DATA_TRANSFER_TASK_INTERVAL 100 /* 100 milliseconds. */
#define HTTP_USER_AGENT APP_TITLE "/" APP_VERSION " (Nintendo Switch)"
#define HTTP_CONNECT_TIMEOUT 10L /* 10 seconds. */
#define HTTP_LOW_SPEED_LIMIT 30L /* 30 bytes per second. */
#define HTTP_LOW_SPEED_TIME HTTP_CONNECT_TIMEOUT
#define HTTP_BUFFER_SIZE 131072L /* 128 KiB. */
#define GITHUB_URL "https://github.com"
#define GITHUB_API_URL "https://api.github.com"
#define GITHUB_REPOSITORY APP_AUTHOR "/" APP_TITLE
#define GITHUB_REPOSITORY_URL GITHUB_URL "/" GITHUB_REPOSITORY
#define GITHUB_NEW_ISSUE_URL GITHUB_REPOSITORY_URL "/issues/new/choose"
#define GITHUB_API_RELEASE_URL GITHUB_API_URL "/repos/" GITHUB_REPOSITORY "/releases/latest"
#define BOREALIS_URL "https://github.com/natinusala/borealis"
#define LIBUSBHSFS_URL "https://github.com/DarkMatterCore/libusbhsfs"
#define FATFS_URL "http://elm-chan.org/fsw/ff/00index_e.html"
#define LZ4_URL "https://github.com/lz4/lz4"
#define JSON_C_URL "https://github.com/json-c/json-c"
#define DISCORD_SERVER_URL "https://discord.gg/SCbbcQx"
// TODO: remove this after the PoC builds are no longer needed.
#define PRERELEASE_URL GITHUB_URL "/" APP_AUTHOR "/nxdumptool/releases/tag/rewrite-prerelease"
#endif /* __DEFINES_H__ */

View File

@ -0,0 +1,325 @@
/*
* async_task.hpp
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* Based on attcs' C++ implementation at:
* https://github.com/attcs/AsyncTask/blob/master/asynctask.h.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __ASYNC_TASK_HPP__
#define __ASYNC_TASK_HPP__
#include <exception>
#include <future>
#include <mutex>
namespace nxdt::tasks
{
/* Used by AsyncTask to throw exceptions whenever required. */
class AsyncTaskException: std::exception
{
public:
enum class eEx: int
{
TaskIsAlreadyRunning, ///< Task is already running.
TaskIsAlreadyFinished, ///< Task is already finished.
TaskIsPending, ///< Task hasn't been executed.
TaskIsCancelled, ///< Task has been cancelled.
TaskWaitTimeout ///< Timed out while waiting for the task to finish.
};
eEx e;
AsyncTaskException() = default;
AsyncTaskException(eEx e) : e(e) { }
};
/* Used by AsyncTask to indicate the current status of the asynchronous task. */
enum class AsyncTaskStatus: int
{
PENDING, ///< The task hasn't been executed yet.
RUNNING, ///< The task is currently running.
FINISHED ///< The task is finished.
};
/* Asynchronous task handler class. */
template<typename Progress, typename Result, typename... Params>
class AsyncTask
{
private:
std::recursive_mutex m_mtx{};
AsyncTaskStatus m_status = AsyncTaskStatus::PENDING;
Result m_result{};
std::future<Result> m_future{};
Progress m_progress{};
bool m_cancelled = false, m_rethrowException = false;
std::exception_ptr m_exceptionPtr{};
/* Runs on the calling thread after DoInBackground() finishes execution. */
void Finish(Result&& result)
{
std::lock_guard<std::recursive_mutex> lock(this->m_mtx);
/* Copy result. */
this->m_result = result;
/* Update status. */
this->m_status = AsyncTaskStatus::FINISHED;
/* Run appropiate post-execution callback. */
if (this->IsCancelled())
{
this->OnCancelled(this->m_result);
} else {
this->OnPostExecute(this->m_result);
}
/* Rethrow asynchronous task exception (if available). */
if (this->m_rethrowException && this->m_exceptionPtr) std::rethrow_exception(this->m_exceptionPtr);
}
protected:
/* Set class as non-copyable and non-moveable. */
NON_COPYABLE(AsyncTask);
NON_MOVEABLE(AsyncTask);
virtual ~AsyncTask() noexcept
{
/* Return right away if the task isn't running. */
if (this->GetStatus() != AsyncTaskStatus::RUNNING) return;
/* Cancel task. This won't do anything if it has already been cancelled. */
this->Cancel();
/* Return right away if the result was already retrieved. */
if (!this->m_future.valid()) return;
/* Wait until a result is provided by the task thread. */
/* Avoid rethrowing any exceptions here -- program execution could end if another exception has already been rethrown. */
m_future.wait();
}
/* Asynchronous task function. */
/* This function should periodically call IsCancelled() to determine if it should end prematurely. */
virtual Result DoInBackground(const Params&... params) = 0;
/* Posts asynchronous task result. Runs on the asynchronous task thread. */
virtual Result PostResult(Result&& result)
{
return std::move(result);
}
/* Cleanup function called if the task is cancelled. Runs on the calling thread. */
virtual void OnCancelled(const Result& result) { }
/* Post-execution function called right after the task finishes. Runs on the calling thread. */
virtual void OnPostExecute(const Result& result) { }
/* Pre-execution function called right before the task starts. Runs on the calling thread. */
virtual void OnPreExecute(void) { }
/* Progress update function. Runs on the calling thread. */
virtual void OnProgressUpdate(const Progress& progress) { }
/* Stores the current progress inside the class. Runs on the asynchronous task thread. */
virtual void PublishProgress(const Progress& progress)
{
std::lock_guard<std::recursive_mutex> lock(this->m_mtx);
/* Don't proceed if the task isn't running. */
if (this->GetStatus() != AsyncTaskStatus::RUNNING || this->IsCancelled()) return;
/* Update progress. */
this->m_progress = progress;
}
/* Returns the current progress. May run on both threads. */
Progress GetProgress(void)
{
std::lock_guard<std::recursive_mutex> lock(this->m_mtx);
return this->m_progress;
}
public:
AsyncTask() = default;
/* Cancels the task. Runs on the calling thread. */
void Cancel(void) noexcept
{
std::lock_guard<std::recursive_mutex> lock(this->m_mtx);
/* Return right away if the task has already completed, or if it has already been cancelled. */
if (this->GetStatus() == AsyncTaskStatus::FINISHED || this->IsCancelled()) return;
/* Update cancel flag. */
this->m_cancelled = true;
}
/* Starts the asynchronous task. Runs on the calling thread. */
AsyncTask<Progress, Result, Params...>& Execute(const Params&... params)
{
/* Return right away if the task was cancelled before starting. */
if (this->IsCancelled()) return *this;
/* Verify task status. */
switch(this->GetStatus())
{
case AsyncTaskStatus::RUNNING:
throw AsyncTaskException(AsyncTaskException::eEx::TaskIsAlreadyRunning);
case AsyncTaskStatus::FINISHED:
throw AsyncTaskException(AsyncTaskException::eEx::TaskIsAlreadyFinished);
default:
break;
}
/* Update task status. */
this->m_status = AsyncTaskStatus::RUNNING;
/* Run pre-execution callback. */
this->OnPreExecute();
/* Start asynchronous task on a new thread. */
this->m_future = std::async(std::launch::async, [this](const Params&... params) -> Result {
/* Catch any exceptions thrown by the asynchronous task. */
try {
return this->PostResult(this->DoInBackground(params...));
} catch(...) {
std::lock_guard<std::recursive_mutex> lock(this->m_mtx);
this->Cancel();
this->m_rethrowException = true;
this->m_exceptionPtr = std::current_exception();
}
return {};
}, params...);
return *this;
}
/* Waits for the asynchronous task to complete, then returns its result. Runs on the calling thread. */
/* If an exception is thrown by the asynchronous task, it will be rethrown by this function. */
Result GetResult(void)
{
auto status = this->GetStatus();
/* Throw an exception if the asynchronous task hasn't been executed. */
if (status == AsyncTaskStatus::PENDING) throw AsyncTaskException(AsyncTaskException::eEx::TaskIsPending);
/* If the task is still running, wait until it finishes. */
/* std::future::get() calls std::future::wait() on its own if the result hasn't been retrieved. */
/* Finish() takes care of rethrowing any exceptions thrown by the asynchronous task. */
if (status == AsyncTaskStatus::RUNNING) this->Finish(this->m_future.get());
/* Throw an exception if the asynchronous task was cancelled. */
if (this->IsCancelled()) throw AsyncTaskException(AsyncTaskException::eEx::TaskIsCancelled);
/* Return result. */
return this->m_result;
}
/* Waits for at most the given time for the asynchronous task to complete, then returns its result. Runs on the calling thread. */
/* If an exception is thrown by the asynchronous task, it will be rethrown by this function. */
template<typename Rep, typename Period>
Result GetResult(const std::chrono::duration<Rep, Period>& timeout)
{
auto status = this->GetStatus();
/* Throw an exception if the asynchronous task hasn't been executed. */
if (status == AsyncTaskStatus::PENDING) throw AsyncTaskException(AsyncTaskException::eEx::TaskIsPending);
/* Check if the task is still running. */
if (status == AsyncTaskStatus::RUNNING)
{
/* Wait for at most the given time for the asynchronous task to complete. */
auto thread_status = this->m_future.wait_for(timeout);
switch(thread_status)
{
case std::future_status::timeout:
/* Throw an exception if we timed out while waiting for the task to finish. */
throw AsyncTaskException(AsyncTaskException::eEx::TaskWaitTimeout);
case std::future_status::ready:
/* Retrieve the task result. */
/* Finish() takes care of rethrowing any exceptions thrown by the asynchronous task. */
this->Finish(this->m_future.get());
/* Throw an exception if the asynchronous task was cancelled. */
if (this->IsCancelled()) throw AsyncTaskException(AsyncTaskException::eEx::TaskIsCancelled);
break;
default:
break;
}
}
/* Return result. */
return this->m_result;
}
/* Returns the current task status. Runs on both threads. */
AsyncTaskStatus GetStatus(void) noexcept
{
return this->m_status;
}
/* Returns true if the task was cancelled before it completed normally. May be used on both threads. */
/* Can be used by the asynchronous task to return prematurely. */
bool IsCancelled(void) noexcept
{
std::lock_guard<std::recursive_mutex> lock(this->m_mtx);
return this->m_cancelled;
}
/* Used by the calling thread to refresh the task progress, preferrably inside a loop. Returns true if the task finished. */
/* If an exception is thrown by the asynchronous task, it will be rethrown by this function. */
bool LoopCallback(void)
{
std::lock_guard<std::recursive_mutex> lock(this->m_mtx);
auto status = this->GetStatus();
/* Return immediately if the task already finished. */
if (status == AsyncTaskStatus::FINISHED) return true;
/* Return immediately if the task hasn't started, or if its result was already retrieved. */
if (status == AsyncTaskStatus::PENDING || !this->m_future.valid()) return false;
/* Get task thread status without waiting. */
auto thread_status = this->m_future.wait_for(std::chrono::seconds(0));
switch(thread_status)
{
case std::future_status::timeout:
/* Update progress. */
this->OnProgressUpdate(this->m_progress);
break;
case std::future_status::ready:
/* Finish task. */
this->Finish(this->m_future.get());
return true;
default:
break;
}
return false;
}
};
}
#endif /* __ASYNC_TASK_HPP__ */

View File

@ -0,0 +1,253 @@
/*
* data_transfer_task.hpp
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __DATA_TRANSFER_TASK_HPP__
#define __DATA_TRANSFER_TASK_HPP__
#include <borealis.hpp>
#include "../core/nxdt_utils.h"
#include "async_task.hpp"
namespace nxdt::tasks
{
/* Used to hold data transfer progress info. */
typedef struct {
size_t total_size; ///< Total size for the data transfer process.
size_t xfer_size; ///< Number of bytes transferred thus far.
int percentage; ///< Progress percentage.
double speed; ///< Current speed expressed in bytes per second.
std::string eta; ///< Formatted ETA string.
} DataTransferProgress;
/* Custom event type used to push data transfer progress updates. */
typedef brls::Event<const DataTransferProgress&> DataTransferProgressEvent;
/* Class template to asynchronously transfer data on a background thread. */
/* Automatically allocates and registers a RepeatingTask on its own, which is started along with the actual task when AsyncTask::execute() is called. */
/* This internal RepeatingTask is guaranteed to work on the UI thread, and it is also automatically unregistered on object destruction. */
/* Progress updates are pushed through a DataTransferProgressEvent. Make sure to register all event listeners before executing the task. */
template<typename Result, typename... Params>
class DataTransferTask: public AsyncTask<DataTransferProgress, Result, Params...>
{
private:
/* Handles task progress updates on the calling thread. */
class Handler: public brls::RepeatingTask
{
private:
bool finished = false;
DataTransferTask<Result, Params...>* task = nullptr;
protected:
void run(retro_time_t current_time) override final
{
brls::RepeatingTask::run(current_time);
if (this->task && !this->finished)
{
this->finished = this->task->LoopCallback();
if (this->finished) this->pause();
}
}
public:
Handler(retro_time_t interval, DataTransferTask<Result, Params...>* task) : brls::RepeatingTask(interval), task(task) { }
ALWAYS_INLINE bool IsFinished(void)
{
return this->finished;
}
};
typedef std::chrono::time_point<std::chrono::steady_clock> SteadyTimePoint;
static constexpr auto &CurrentSteadyTimePoint = std::chrono::steady_clock::now;
DataTransferProgressEvent progress_event{};
Handler *task_handler = nullptr;
SteadyTimePoint start_time{}, prev_time{}, end_time{};
size_t prev_xfer_size = 0;
bool first_publish_progress = true;
ALWAYS_INLINE std::string FormatTimeString(double seconds)
{
return fmt::format("{:02.0F}H{:02.0F}M{:02.0F}S", std::fmod(seconds, 86400.0) / 3600.0, std::fmod(seconds, 3600.0) / 60.0, std::fmod(seconds, 60.0));
}
void PostExecutionCallback(void)
{
/* Set end time. */
this->end_time = CurrentSteadyTimePoint();
/* Fire task handler immediately to make it store the last result from AsyncTask::LoopCallback(). */
/* We do this here because all subscribers to our progress event will most likely call IsFinished() to check if the task is complete. */
/* That being the case, if the `finished` flag returned by the task handler isn't updated before the progress event subscribers receive the last progress update, */
/* they won't be able to determine if the task has already finished, leading to unsuspected consequences. */
this->task_handler->fireNow();
/* Update progress one last time. */
/* This will effectively invoke the callbacks from all of our progress event subscribers. */
this->OnProgressUpdate(this->GetProgress());
/* Unset long running process state. */
utilsSetLongRunningProcessState(false);
}
protected:
/* Set class as non-copyable and non-moveable. */
NON_COPYABLE(DataTransferTask);
NON_MOVEABLE(DataTransferTask);
/* Runs on the calling thread. */
void OnCancelled(const Result& result) override final
{
NX_IGNORE_ARG(result);
/* Run post execution callback. */
this->PostExecutionCallback();
}
/* Runs on the calling thread. */
void OnPostExecute(const Result& result) override final
{
NX_IGNORE_ARG(result);
/* Run post execution callback. */
this->PostExecutionCallback();
}
/* Runs on the calling thread. */
void OnPreExecute(void) override final
{
/* Set long running process state. */
utilsSetLongRunningProcessState(true);
/* Start task handler. */
this->task_handler->start();
/* Set start time. */
this->start_time = this->prev_time = CurrentSteadyTimePoint();
}
/* Runs on the calling thread. */
void OnProgressUpdate(const DataTransferProgress& progress) override final
{
/* Return immediately if there has been no progress at all. */
bool proceed = (progress.xfer_size > prev_xfer_size || (progress.xfer_size == prev_xfer_size && (!progress.total_size || progress.xfer_size >= progress.total_size ||
this->first_publish_progress)));
if (!proceed) return;
/* Calculate time difference between the last progress update and the current one. */
/* Return immediately if the task hasn't been cancelled and less than 1 second has passed since the last progress update -- but only if */
/* this isn't the last chunk *or* if we don't know the total size and the task is still running . */
AsyncTaskStatus status = this->GetStatus();
SteadyTimePoint cur_time = std::chrono::steady_clock::now();
double diff_time = std::chrono::duration<double>(cur_time - this->prev_time).count();
if (!this->IsCancelled() && diff_time < 1.0 && ((progress.total_size && progress.xfer_size < progress.total_size) || status == AsyncTaskStatus::RUNNING)) return;
/* Calculate transferred data size difference between the last progress update and the current one. */
double diff_xfer_size = static_cast<double>(progress.xfer_size - prev_xfer_size);
/* Calculate transfer speed in bytes per second. */
double speed = (diff_xfer_size / diff_time);
/* Fill struct. */
DataTransferProgress new_progress = progress;
new_progress.speed = speed;
if (progress.total_size && speed > 0.0)
{
/* Calculate remaining data size and ETA if we know the total size. */
double remaining = static_cast<double>(progress.total_size - progress.xfer_size);
double eta = (remaining / speed);
new_progress.eta = this->FormatTimeString(eta);
} else {
/* No total size nor speed means no ETA calculation, sadly. */
new_progress.eta = "";
}
/* Set total size if we don't know it and if this is the final chunk. */
if (!new_progress.total_size && status == AsyncTaskStatus::FINISHED)
{
new_progress.total_size = new_progress.xfer_size;
new_progress.percentage = 100;
}
/* Update class variables. */
this->prev_time = cur_time;
this->prev_xfer_size = progress.xfer_size;
if (this->first_publish_progress) this->first_publish_progress = false;
/* Send updated progress to all listeners. */
this->progress_event.fire(new_progress);
}
public:
DataTransferTask()
{
/* Create task handler. */
this->task_handler = new Handler(DATA_TRANSFER_TASK_INTERVAL, this);
}
~DataTransferTask()
{
/* Stop task handler. Borealis' task manager will take care of deleting it. */
this->task_handler->stop();
/* Unregister all event listeners. */
this->progress_event.unsubscribeAll();
}
/* Returns the last result from AsyncTask::LoopCallback(). Runs on the calling thread. */
ALWAYS_INLINE bool IsFinished(void)
{
return this->task_handler->IsFinished();
}
/* Returns the task duration expressed in seconds. */
/* If the task hasn't finished yet, it returns the number of seconds that have passed since the task was started. */
ALWAYS_INLINE double GetDuration(void)
{
return std::chrono::duration<double>(this->IsFinished() ? (this->end_time - this->start_time) : (CurrentSteadyTimePoint() - this->start_time)).count();
}
/* Returns a human-readable string that represents the task duration. */
/* If the task hasn't finished yet, the string represents the time that has passed since the task was started. */
ALWAYS_INLINE std::string GetDurationString(void)
{
return this->FormatTimeString(this->GetDuration());
}
ALWAYS_INLINE DataTransferProgressEvent::Subscription RegisterListener(DataTransferProgressEvent::Callback cb)
{
return this->progress_event.subscribe(cb);
}
ALWAYS_INLINE void UnregisterListener(DataTransferProgressEvent::Subscription subscription)
{
this->progress_event.unsubscribe(subscription);
}
};
}
#endif /* __DATA_TRANSFER_TASK_HPP__ */

View File

@ -0,0 +1,60 @@
/*
* download_data_task.hpp
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __DOWNLOAD_DATA_TASK_HPP__
#define __DOWNLOAD_DATA_TASK_HPP__
#include "download_task.hpp"
namespace nxdt::tasks
{
/* Used to hold a buffer + size pair with downloaded data. */
typedef std::pair<char*, size_t> DownloadDataResult;
/* Asynchronous task used to store downloaded data into a dynamically allocated buffer using a URL. */
/* The buffer returned by std::pair::first() must be manually freed by the caller using free(). */
class DownloadDataTask: public DownloadTask<DownloadDataResult, std::string, bool>
{
protected:
/* Set class as non-copyable and non-moveable. */
NON_COPYABLE(DownloadDataTask);
NON_MOVEABLE(DownloadDataTask);
/* Runs in the background thread. */
DownloadDataResult DoInBackground(const std::string& url, const bool& force_https) override final
{
char *buf = nullptr;
size_t buf_size = 0;
/* If the process fails or if it's cancelled, httpDownloadData() will take care of freeing up the allocated memory and returning NULL. */
buf = httpDownloadData(&buf_size, url.c_str(), force_https, DownloadDataTask::HttpProgressCallback, this);
return std::make_pair(buf, buf_size);
}
public:
DownloadDataTask() = default;
};
}
#endif /* __DOWNLOAD_DATA_TASK_HPP__ */

View File

@ -0,0 +1,51 @@
/*
* download_file_task.hpp
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __DOWNLOAD_FILE_TASK_HPP__
#define __DOWNLOAD_FILE_TASK_HPP__
#include "download_task.hpp"
namespace nxdt::tasks
{
/* Asynchronous task used to download a file using an output path and a URL. */
class DownloadFileTask: public DownloadTask<bool, std::string, std::string, bool>
{
protected:
/* Set class as non-copyable and non-moveable. */
NON_COPYABLE(DownloadFileTask);
NON_MOVEABLE(DownloadFileTask);
/* Runs in the background thread. */
bool DoInBackground(const std::string& path, const std::string& url, const bool& force_https) override final
{
/* If the process fails or if it's cancelled, httpDownloadFile() will take care of closing the incomplete output file and deleting it. */
return httpDownloadFile(path.c_str(), url.c_str(), force_https, DownloadFileTask::HttpProgressCallback, this);
}
public:
DownloadFileTask() = default;
};
}
#endif /* __DOWNLOAD_FILE_TASK_HPP__ */

View File

@ -0,0 +1,70 @@
/*
* download_task.hpp
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __DOWNLOAD_TASK_HPP__
#define __DOWNLOAD_TASK_HPP__
#include "data_transfer_task.hpp"
namespace nxdt::tasks
{
/* Class template to asynchronously download data on a background thread. */
/* Uses both AsyncTask and DataTransferTask class templates. */
template<typename Result, typename... Params>
class DownloadTask: public DataTransferTask<Result, Params...>
{
protected:
/* Set class as non-copyable and non-moveable. */
NON_COPYABLE(DownloadTask);
NON_MOVEABLE(DownloadTask);
public:
DownloadTask() = default;
/* Runs on the asynchronous task thread. Required by cURL. */
/* Make sure to pass it to either httpDownloadFile() or httpDownloadData() with 'this' as the user pointer. */
static int HttpProgressCallback(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
{
NX_IGNORE_ARG(ultotal);
NX_IGNORE_ARG(ulnow);
DataTransferProgress progress{};
DownloadTask<Result, Params...>* task = static_cast<DownloadTask<Result, Params...>*>(clientp);
/* Don't proceed if we're dealing with an invalid task pointer, or if the task has been cancelled. */
if (!task || task->IsCancelled()) return 1;
/* Fill struct. */
progress.total_size = static_cast<size_t>(dltotal);
progress.xfer_size = static_cast<size_t>(dlnow);
progress.percentage = (progress.total_size ? static_cast<int>((progress.xfer_size * 100) / progress.total_size) : 0);
/* Push progress onto the class. */
task->PublishProgress(progress);
return 0;
}
};
}
#endif /* __DOWNLOAD_TASK_HPP__ */

View File

@ -0,0 +1,74 @@
/*
* gamecard_image_dump_task.hpp
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __GAMECARD_IMAGE_DUMP_TASK_HPP__
#define __GAMECARD_IMAGE_DUMP_TASK_HPP__
#include <optional>
#include <mutex>
#include "data_transfer_task.hpp"
namespace nxdt::tasks
{
typedef std::optional<std::string> GameCardDumpTaskError;
/* Generates an image dump out of the inserted gamecard. */
class GameCardImageDumpTask: public DataTransferTask<GameCardDumpTaskError, std::string, bool, bool, bool, bool, bool>
{
private:
std::mutex task_mtx;
bool calculate_checksum = false, lookup_checksum = false;
u32 gc_img_crc = 0, full_gc_img_crc = 0;
protected:
/* Set class as non-copyable and non-moveable. */
NON_COPYABLE(GameCardImageDumpTask);
NON_MOVEABLE(GameCardImageDumpTask);
/* Runs in the background thread. */
GameCardDumpTaskError DoInBackground(const std::string& output_path, const bool& prepend_key_area, const bool& keep_certificate, const bool& trim_dump,
const bool& calculate_checksum, const bool& lookup_checksum) override final;
public:
GameCardImageDumpTask() = default;
/* Returns the CRC32 calculated over the gamecard image. */
/* Returns zero if checksum calculation wasn't enabled, if the task hasn't finished yet or if the task was cancelled. */
ALWAYS_INLINE u32 GetImageChecksum(void)
{
std::scoped_lock lock(this->task_mtx);
return ((this->calculate_checksum && this->IsFinished() && !this->IsCancelled()) ? this->gc_img_crc : 0);
}
/* Returns the CRC32 calculated over the gamecard image with prepended key area data. */
/* Returns zero if checksum calculation wasn't enabled, if the task hasn't finished yet or if the task was cancelled. */
ALWAYS_INLINE u32 GetFullImageChecksum(void)
{
std::scoped_lock lock(this->task_mtx);
return ((this->calculate_checksum && this->IsFinished() && !this->IsCancelled()) ? this->full_gc_img_crc : 0);
}
};
}
#endif /* __GAMECARD_IMAGE_DUMP_TASK_HPP__ */

View File

@ -0,0 +1,72 @@
/*
* gamecard_status_task.hpp
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __GAMECARD_STATUS_TASK_HPP__
#define __GAMECARD_STATUS_TASK_HPP__
#include <borealis.hpp>
#include "../core/nxdt_utils.h"
#include "../core/gamecard.h"
namespace nxdt::tasks
{
/* Custom event type. */
typedef brls::Event<const GameCardStatus&> GameCardStatusEvent;
/* Gamecard status task. */
/* Its event provides a const reference to a GameCardStatus value. */
class GameCardStatusTask: public brls::RepeatingTask
{
private:
GameCardStatusEvent gc_status_event;
GameCardStatus cur_gc_status = GameCardStatus_NotInserted;
GameCardStatus prev_gc_status = GameCardStatus_NotInserted;
bool skip_notification = true;
protected:
void run(retro_time_t current_time) override;
public:
GameCardStatusTask();
~GameCardStatusTask();
/* Intentionally left here to let views retrieve title metadata on-demand. */
ALWAYS_INLINE const GameCardStatus& GetGameCardStatus(void)
{
return this->cur_gc_status;
}
ALWAYS_INLINE GameCardStatusEvent::Subscription RegisterListener(GameCardStatusEvent::Callback cb)
{
return this->gc_status_event.subscribe(cb);
}
ALWAYS_INLINE void UnregisterListener(GameCardStatusEvent::Subscription subscription)
{
this->gc_status_event.unsubscribe(subscription);
}
};
}
#endif /* __GAMECARD_STATUS_TASK_HPP__ */

View File

@ -0,0 +1,78 @@
/*
* status_info_task.hpp
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __STATUS_INFO_TASK_HPP__
#define __STATUS_INFO_TASK_HPP__
#include <borealis.hpp>
#include "../core/nxdt_utils.h"
namespace nxdt::tasks
{
/* Used to hold status info data. */
typedef struct {
struct tm timeinfo;
u32 charge_percentage;
PsmChargerType charger_type;
bool connected;
NifmInternetConnectionType connection_type;
char ip_addr[16];
} StatusInfoData;
/* Custom event type. */
typedef brls::Event<const StatusInfoData&> StatusInfoEvent;
/* Status info task. */
/* Its event provides a const reference to a StatusInfoData struct. */
class StatusInfoTask: public brls::RepeatingTask
{
private:
StatusInfoEvent status_info_event;
StatusInfoData status_info_data{};
protected:
void run(retro_time_t current_time) override;
public:
StatusInfoTask();
~StatusInfoTask();
ALWAYS_INLINE bool IsInternetConnectionAvailable(void)
{
return this->status_info_data.connected;
}
ALWAYS_INLINE StatusInfoEvent::Subscription RegisterListener(StatusInfoEvent::Callback cb)
{
return this->status_info_event.subscribe(cb);
}
ALWAYS_INLINE void UnregisterListener(StatusInfoEvent::Subscription subscription)
{
this->status_info_event.unsubscribe(subscription);
}
};
}
#endif /* __STATUS_INFO_TASK_HPP__ */

View File

@ -0,0 +1,80 @@
/*
* title_metadata_task.hpp
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __TITLE_METADATA_TASK_HPP__
#define __TITLE_METADATA_TASK_HPP__
#include <borealis.hpp>
#include "../core/nxdt_utils.h"
#include "../core/title.h"
namespace nxdt::tasks
{
/* Used to hold an application metadata array + its number of elements. */
typedef struct {
TitleApplicationMetadata **app_metadata;
u32 app_metadata_count;
} TitleApplicationMetadataInfo;
/* Custom event type. */
typedef brls::Event<const TitleApplicationMetadataInfo&> UserTitleEvent;
/* Title metadata task. */
/* Its event provides a const reference to a TitleApplicationMetadataInfo with metadata for user titles (system titles don't change at runtime). */
class TitleMetadataTask: public brls::RepeatingTask
{
private:
UserTitleEvent user_title_event;
TitleApplicationMetadataInfo system_metadata_info{};
TitleApplicationMetadataInfo user_metadata_info{};
void PopulateApplicationMetadataInfo(bool is_system);
protected:
void run(retro_time_t current_time) override;
public:
TitleMetadataTask();
~TitleMetadataTask();
/* Intentionally left here to let views retrieve title metadata on-demand. */
ALWAYS_INLINE const TitleApplicationMetadataInfo& GetApplicationMetadataInfo(bool is_system)
{
return (is_system ? this->system_metadata_info : this->user_metadata_info);
}
ALWAYS_INLINE UserTitleEvent::Subscription RegisterListener(UserTitleEvent::Callback cb)
{
return this->user_title_event.subscribe(cb);
}
ALWAYS_INLINE void UnregisterListener(UserTitleEvent::Subscription subscription)
{
this->user_title_event.unsubscribe(subscription);
}
};
}
#endif /* __TITLE_METADATA_TASK_HPP__ */

View File

@ -0,0 +1,80 @@
/*
* ums_task.hpp
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __UMS_TASK_HPP__
#define __UMS_TASK_HPP__
#include <borealis.hpp>
#include "../core/nxdt_utils.h"
#include "../core/ums.h"
namespace nxdt::tasks
{
/* Used to hold information from UMS devices. */
typedef std::pair<const UsbHsFsDevice*, std::string> UmsDeviceVectorEntry;
typedef std::vector<UmsDeviceVectorEntry> UmsDeviceVector;
/* Custom event type. */
typedef brls::Event<const UmsDeviceVector&> UmsEvent;
/* USB Mass Storage task. */
/* Its event provides a const reference to a UmsDeviceVector. */
class UmsTask: public brls::RepeatingTask
{
private:
UmsEvent ums_event;
UsbHsFsDevice *ums_devices = nullptr;
u32 ums_devices_count = 0;
UmsDeviceVector ums_devices_vector{};
void PopulateUmsDeviceVector(void);
protected:
void run(retro_time_t current_time) override;
public:
UmsTask();
~UmsTask();
/* Intentionally left here to let views retrieve UMS device info on-demand. */
ALWAYS_INLINE const UmsDeviceVector& GetUmsDevices(void)
{
return this->ums_devices_vector;
}
ALWAYS_INLINE UmsEvent::Subscription RegisterListener(UmsEvent::Callback cb)
{
return this->ums_event.subscribe(cb);
}
ALWAYS_INLINE void UnregisterListener(UmsEvent::Subscription subscription)
{
this->ums_event.unsubscribe(subscription);
}
};
}
#endif /* __UMS_TASK_HPP__ */

View File

@ -0,0 +1,70 @@
/*
* usb_host_task.hpp
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __USB_HOST_TASK_HPP__
#define __USB_HOST_TASK_HPP__
#include <borealis.hpp>
#include "../core/nxdt_utils.h"
#include "../core/usb.h"
namespace nxdt::tasks
{
/* Custom event type. */
typedef brls::Event<const UsbHostSpeed&> UsbHostEvent;
/* USB host device connection task. */
class UsbHostTask: public brls::RepeatingTask
{
private:
UsbHostEvent usb_host_event;
UsbHostSpeed cur_usb_host_speed = UsbHostSpeed_None;
UsbHostSpeed prev_usb_host_speed = UsbHostSpeed_None;
protected:
void run(retro_time_t current_time) override;
public:
UsbHostTask();
~UsbHostTask();
/* Intentionally left here to let views retrieve USB host connection speed on-demand. */
ALWAYS_INLINE const UsbHostSpeed& GetUsbHostSpeed(void)
{
return this->cur_usb_host_speed;
}
ALWAYS_INLINE UsbHostEvent::Subscription RegisterListener(UsbHostEvent::Callback cb)
{
return this->usb_host_event.subscribe(cb);
}
ALWAYS_INLINE void UnregisterListener(UsbHostEvent::Subscription subscription)
{
this->usb_host_event.unsubscribe(subscription);
}
};
}
#endif /* __USB_HOST_TASK_HPP__ */

View File

@ -0,0 +1,96 @@
/*
* file_writer.hpp
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __FILE_WRITER_HPP__
#define __FILE_WRITER_HPP__
#include <borealis.hpp>
#include <optional>
#include "../core/nxdt_utils.h"
#include "../core/usb.h"
namespace nxdt::utils
{
/* Writes output files to different storage locations based on the provided input path. */
/* It also handles file splitting in FAT-based UMS volumes. */
class FileWriter
{
public:
/* Determines the output storage type used by this file. */
typedef enum : u8 {
None = 0,
SdCard = 1,
UsbHost = 2,
UmsDevice = 3
} StorageType;
private:
std::string output_path{};
size_t total_size = 0, cur_size = 0;
u32 nsp_header_size = 0;
bool nsp_header_written = false;
StorageType storage_type = StorageType::None;
bool split_file = false, file_created = false, file_closed = false;
FILE *fp = nullptr;
u8 split_file_part_cnt = 0, split_file_part_idx = 0;
size_t split_file_part_size = 0;
std::optional<std::string> CheckFreeSpace(void);
void CloseCurrentFile(void);
bool OpenNextFile(void);
bool CreateInitialFile(void);
protected:
/* Set class as non-copyable and non-moveable. */
NON_COPYABLE(FileWriter);
NON_MOVEABLE(FileWriter);
public:
FileWriter(const std::string& output_path, const size_t& total_size, const u32& nsp_header_size = 0);
~FileWriter();
/* Writes data to the output file. */
/* Takes care of seamlessly switching to a new part file if needed. */
bool Write(const void *data, const size_t& data_size);
/* Writes NSP header data to offset 0. */
/* Only valid if dealing with a NSP file. */
bool WriteNspHeader(const void *nsp_header, const u32& nsp_header_size);
/* Closes the file and deletes it if it's incomplete (or if forcefully requested). */
void Close(bool force_delete = false);
/* Returns the storage type for this file. */
StorageType GetStorageType(void);
};
}
#endif /* __FILE_WRITER_HPP__ */

View File

@ -0,0 +1,55 @@
/*
* is_base_of_template.hpp
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* Based on goneskiing's C++ implementation at:
* https://stackoverflow.com/a/63562826
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __IS_BASE_OF_TEMPLATE_HPP__
#define __IS_BASE_OF_TEMPLATE_HPP__
#include <variant>
#include <experimental/type_traits>
namespace nxdt::utils
{
/* Can be used as part of static asserts to check if any given class was derived from a base template class. */
template <template <typename...> class Base, typename Derived>
struct is_base_of_template
{
template <typename... Ts> static constexpr std::variant<Ts...> is_callable(Base<Ts...>*);
template <typename T> using is_callable_t = decltype(is_callable(std::declval<T*>()));
static inline constexpr bool value = std::experimental::is_detected_v<is_callable_t, Derived>;
using type = std::experimental::detected_or_t<void, is_callable_t, Derived>;
};
template <template <typename...> class Base, typename Derived>
using is_base_of_template_t = typename is_base_of_template<Base, Derived>::type;
template <template <typename...> class Base, typename Derived>
inline constexpr bool is_base_of_template_v = is_base_of_template<Base, Derived>::value;
}
#endif /* __IS_BASE_OF_TEMPLATE_HPP__ */

View File

@ -0,0 +1,80 @@
/*
* scope_guard.hpp
*
* Copyright (c) 2018-2021, SciresM.
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* Scope guard logic lovingly taken from Andrei Alexandrescu's "Systemic Error Handling in C++".
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __SCOPE_GUARD_HPP__
#define __SCOPE_GUARD_HPP__
#include "../defines.h"
#define SCOPE_GUARD ::nxdt::utils::ScopeGuardOnExit() + [&]() ALWAYS_INLINE_LAMBDA
#define ON_SCOPE_EXIT auto ANONYMOUS_VARIABLE(SCOPE_EXIT_STATE_) = SCOPE_GUARD
namespace nxdt::utils {
template<class F>
class ScopeGuard {
NON_COPYABLE(ScopeGuard);
private:
F f;
bool active;
public:
constexpr ALWAYS_INLINE ScopeGuard(F f) : f(std::move(f)), active(true) {}
constexpr ALWAYS_INLINE ~ScopeGuard()
{
if (active) f();
}
constexpr ALWAYS_INLINE void Cancel()
{
active = false;
}
constexpr ALWAYS_INLINE ScopeGuard(ScopeGuard&& rhs) : f(std::move(rhs.f)), active(rhs.active)
{
rhs.Cancel();
}
ScopeGuard &operator=(ScopeGuard&& rhs) = delete;
};
template<class F>
constexpr ALWAYS_INLINE ScopeGuard<F> MakeScopeGuard(F f)
{
return ScopeGuard<F>(std::move(f));
}
enum class ScopeGuardOnExit {};
template <typename F>
constexpr ALWAYS_INLINE ScopeGuard<F> operator+(ScopeGuardOnExit, F&& f)
{
return ScopeGuard<F>(std::forward<F>(f));
}
}
#endif /* __SCOPE_GUARD_HPP__ */

View File

@ -0,0 +1,58 @@
/*
* about_tab.hpp
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __ABOUT_TAB_HPP__
#define __ABOUT_TAB_HPP__
#include <borealis.hpp>
#include "focusable_item.hpp"
namespace nxdt::views
{
/* Extended class to display a focusable and optionally centered (but unhighlightable) label. */
class AboutTabLabel: public FocusableLabel
{
public:
AboutTabLabel(brls::LabelStyle labelStyle, std::string text, bool center = false) : FocusableLabel(false, false, labelStyle, text, true)
{
if (center) this->setHorizontalAlign(NVG_ALIGN_CENTER);
}
};
class AboutTab: public brls::List
{
private:
brls::Image *logo = nullptr;
protected:
void draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, brls::Style* style, brls::FrameContext* ctx) override;
void layout(NVGcontext* vg, brls::Style* style, brls::FontStash* stash) override;
public:
AboutTab();
~AboutTab();
};
}
#endif /* __ABOUT_TAB_HPP__ */

View File

@ -0,0 +1,55 @@
/*
* data_transfer_progress_display.hpp
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __DATA_TRANSFER_PROGRESS_DISPLAY_HPP__
#define __DATA_TRANSFER_PROGRESS_DISPLAY_HPP__
#include "../tasks/data_transfer_task.hpp"
namespace nxdt::views
{
/* Used to display the progress of an ongoing data transfer task. Shows a progress bar, a spinner, a percentage value, the process speed and an ETA value. */
class DataTransferProgressDisplay: public brls::View
{
private:
brls::ProgressDisplay *progress_display = nullptr;
brls::Label *size_lbl = nullptr, *speed_eta_lbl = nullptr;
std::string GetFormattedSizeString(double size);
protected:
void draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, brls::Style* style, brls::FrameContext* ctx) override;
void layout(NVGcontext* vg, brls::Style* style, brls::FontStash* stash) override;
public:
DataTransferProgressDisplay();
~DataTransferProgressDisplay();
void SetProgress(const nxdt::tasks::DataTransferProgress& progress);
void willAppear(bool resetState = false) override;
void willDisappear(bool resetState = false) override;
};
}
#endif /* __DATA_TRANSFER_PROGRESS_DISPLAY_HPP__ */

View File

@ -0,0 +1,177 @@
/*
* data_transfer_task_frame.hpp
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __DATA_TRANSFER_TASK_FRAME_HPP__
#define __DATA_TRANSFER_TASK_FRAME_HPP__
#include "../utils/is_base_of_template.hpp"
#include "error_frame.hpp"
#include "data_transfer_progress_display.hpp"
namespace nxdt::views
{
template<typename Task>
class DataTransferTaskFrame: public brls::AppletFrame
{
static_assert(nxdt::utils::is_base_of_template_v<nxdt::tasks::DataTransferTask, Task>, "Task must inherit from DataTransferTask");
protected:
Task task;
private:
DataTransferProgressDisplay *task_progress = nullptr;
ErrorFrame *error_frame = nullptr;
bool progress_displayed = false;
std::string notification{};
void DisplayProgress(void)
{
this->setContentView(this->task_progress);
this->progress_displayed = true;
}
void DisplayError(const std::string& msg)
{
this->error_frame->SetMessage(msg);
this->setContentView(this->error_frame);
this->progress_displayed = false;
}
template<typename... Params>
void Initialize(const std::string& title, brls::Image *icon, const Params&... params)
{
/* Set UI properties. */
this->setTitle(title);
this->setIcon(icon);
/* Update B button label. */
this->updateActionHint(brls::Key::B, brls::i18n::getStr("generic/cancel"));
/* Initialize progress display. */
this->task_progress = new DataTransferProgressDisplay();
/* Initialize error frame. */
this->error_frame = new ErrorFrame();
/* Subscribe to the background task. */
this->task.RegisterListener([this](const nxdt::tasks::DataTransferProgress& progress) {
/* Store notification message and return immediately if the background task was cancelled. */
if (this->task.IsCancelled())
{
this->notification = brls::i18n::getStr("generic/process_cancelled");
return;
}
/* Update progress. */
this->task_progress->SetProgress(progress);
/* Check if the background task has finished. */
if (this->task.IsFinished())
{
/* Get background task result and error reason. */
std::string error_msg{};
bool ret = this->GetTaskResult(error_msg);
if (ret)
{
/* Store notification message. */
this->notification = brls::i18n::getStr("generic/process_complete");
/* Pop view. */
this->onCancel();
} else {
/* Update B button label. */
this->updateActionHint(brls::Key::B, brls::i18n::getStr("brls/hints/back"));
/* Display error frame. */
this->DisplayError(error_msg);
}
}
});
/* Start background task. */
this->task.Execute(params...);
/* Set content view. */
this->DisplayProgress();
}
protected:
/* Set class as non-copyable and non-moveable. */
NON_COPYABLE(DataTransferTaskFrame);
NON_MOVEABLE(DataTransferTaskFrame);
bool onCancel(void) override final
{
/* Cancel background task. This will have no effect if the background task already finished or if it was already cancelled. */
this->task.Cancel();
/* Pop view. This will invoke this class' destructor. */
brls::Application::popView(brls::ViewAnimation::SLIDE_RIGHT);
return true;
}
/* Must be implemented by derived classes to determine if the background task succeeded or not by calling GetResult() on their own. */
/* If the task failed, false shall be returned and `error_msg` shall be updated to reflect the error reason. */
virtual bool GetTaskResult(std::string& error_msg) = 0;
public:
template<typename... Params>
DataTransferTaskFrame(const std::string& title, const Params&... params) : brls::AppletFrame(true, true)
{
/* Generate icon using the default image. */
brls::Image *icon = new brls::Image();
icon->setImage(BOREALIS_ASSET("icon/" APP_TITLE ".jpg"));
icon->setScaleType(brls::ImageScaleType::SCALE);
/* Initialize the rest of the elements. */
this->Initialize(title, icon, params...);
}
template<typename... Params>
DataTransferTaskFrame(const std::string& title, brls::Image *icon, const Params&... params) : brls::AppletFrame(true, true)
{
/* Initialize the rest of the elements. */
this->Initialize(title, icon, params...);
}
~DataTransferTaskFrame()
{
/* Delete the view that's not currently being displayed. */
/* The other one will be taken care of by brls::AppletFrame's destructor. */
if (this->progress_displayed)
{
delete this->error_frame;
} else {
delete this->task_progress;
}
/* Show relevant notification, if needed. */
/* This is done here to avoid a slowdown issue while attempting to pop the current view and display a notification at the same time. */
if (!this->notification.empty()) brls::Application::notify(this->notification);
}
};
}
#endif /* __DATA_TRANSFER_TASK_FRAME_HPP__ */

View File

@ -0,0 +1,92 @@
/*
* dump_options_frame.hpp
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __DUMP_OPTIONS_FRAME_HPP__
#define __DUMP_OPTIONS_FRAME_HPP__
#include <borealis.hpp>
#include "root_view.hpp"
namespace nxdt::views
{
class DumpOptionsFrame: public brls::ThumbnailFrame
{
protected:
RootView *root_view = nullptr;
private:
std::string storage_prefix{}, base_output_path{}, raw_filename{};
brls::List *list = nullptr;
brls::InputListItem *filename = nullptr;
brls::SelectListItem *output_storage = nullptr;
brls::GenericEvent *button_click_event = nullptr;
nxdt::tasks::UmsEvent::Subscription ums_task_sub;
bool finalized = false;
void Initialize(const std::string& title, brls::Image *icon);
std::string SanitizeUserFileName(void);
std::vector<std::string> GenerateOutputStoragesVector(const nxdt::tasks::UmsDeviceVector& ums_devices);
void UpdateStoragePrefix(u32 selected);
protected:
DumpOptionsFrame(RootView *root_view, const std::string& title, const std::string& base_output_path, const std::string& raw_filename);
DumpOptionsFrame(RootView *root_view, const std::string& title, brls::Image *icon, const std::string& base_output_path, const std::string& raw_filename);
~DumpOptionsFrame();
bool onCancel(void) override final;
void addView(brls::View *view, bool fill = false);
bool GetOutputFilePath(const std::string& extension, std::string& output);
ALWAYS_INLINE brls::GenericEvent::Subscription RegisterButtonListener(brls::GenericEvent::Callback cb)
{
return this->button_click_event->subscribe([this, cb](brls::View *view){
/* Check if the USB host is currently selected as the output storage. */
/* If so, we'll prevent the provided callback from running. */
if (this->output_storage->getSelectedValue() == ConfigOutputStorage_UsbHost && this->root_view->GetUsbHostSpeed() == UsbHostSpeed_None)
{
brls::Application::notify(brls::i18n::getStr("dump_options/notifications/usb_host_unavailable"));
return;
}
/* Run the provided callback. */
cb(view);
});
}
ALWAYS_INLINE void UnregisterButtonListener(brls::GenericEvent::Subscription subscription)
{
this->button_click_event->unsubscribe(subscription);
}
};
}
#endif /* __DUMP_OPTIONS_FRAME_HPP__ */

View File

@ -0,0 +1,50 @@
/*
* error_frame.hpp
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* Based on crash_frame.hpp from Borealis.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __ERROR_FRAME_HPP__
#define __ERROR_FRAME_HPP__
#include <borealis.hpp>
namespace nxdt::views
{
class ErrorFrame: public brls::View
{
private:
brls::Label *label = nullptr;
protected:
void draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, brls::Style* style, brls::FrameContext* ctx) override;
void layout(NVGcontext* vg, brls::Style* style, brls::FontStash* stash) override;
public:
ErrorFrame(const std::string& msg = "");
~ErrorFrame();
void SetMessage(const std::string& msg);
};
}
#endif /* __ERROR_FRAME_HPP__ */

View File

@ -0,0 +1,87 @@
/*
* focusable_item.hpp
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __FOCUSABLE_ITEM_HPP__
#define __FOCUSABLE_ITEM_HPP__
#include <borealis.hpp>
#include <type_traits>
namespace nxdt::views
{
template<typename ViewType>
class FocusableItem: public ViewType
{
static_assert(std::is_base_of_v<brls::View, ViewType>, "ViewType must inherit from brls::View");
private:
bool highlight, highlight_bg;
protected:
brls::View* getDefaultFocus(void) override
{
return this;
}
bool isHighlightBackgroundEnabled(void) override
{
return this->highlight_bg;
}
void onFocusGained(void) override
{
if (this->highlight)
{
/* Focus and highlight view. */
brls::View::onFocusGained();
} else {
/* Focus view without highlighting it. */
this->focused = true;
this->focusEvent.fire(this);
if (this->hasParent()) this->getParent()->onChildFocusGained(this);
}
}
public:
template<typename... Types>
FocusableItem(bool highlight, bool highlight_bg, Types... args) : ViewType(args...), highlight(highlight), highlight_bg(highlight_bg) { }
};
/* Define templated classes for the focusable items we're gonna use. */
class FocusableLabel: public FocusableItem<brls::Label>
{
public:
template<typename... Types>
FocusableLabel(bool highlight, bool highlight_bg, Types... args) : FocusableItem<brls::Label>(highlight, highlight_bg, args...) { }
};
class FocusableTable: public FocusableItem<brls::Table>
{
public:
template<typename... Types>
FocusableTable(bool highlight, bool highlight_bg, Types... args) : FocusableItem<brls::Table>(highlight, highlight_bg, args...) { }
};
}
#endif /* __FOCUSABLE_ITEM_HPP__ */

View File

@ -0,0 +1,59 @@
/*
* gamecard_image_dump_options_frame.hpp
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __GAMECARD_IMAGE_DUMP_OPTIONS_FRAME_HPP__
#define __GAMECARD_IMAGE_DUMP_OPTIONS_FRAME_HPP__
#include "dump_options_frame.hpp"
namespace nxdt::views
{
class GameCardImageDumpOptionsFrame: public DumpOptionsFrame
{
private:
nxdt::tasks::GameCardStatusEvent::Subscription gc_task_sub;
brls::VoidEvent gc_ejected_event;
brls::ToggleListItem *prepend_key_area = nullptr;
brls::ToggleListItem *keep_certificate = nullptr;
brls::ToggleListItem *trim_dump = nullptr;
brls::ToggleListItem *calculate_checksum = nullptr;
brls::ToggleListItem *lookup_checksum = nullptr;
public:
GameCardImageDumpOptionsFrame(RootView *root_view, std::string raw_filename);
~GameCardImageDumpOptionsFrame();
ALWAYS_INLINE brls::VoidEvent::Subscription RegisterGameCardEjectionListener(brls::VoidEvent::Callback cb)
{
return this->gc_ejected_event.subscribe(cb);
}
ALWAYS_INLINE void UnregisterGameCardEjectionListener(brls::VoidEvent::Subscription subscription)
{
this->gc_ejected_event.unsubscribe(subscription);
}
};
}
#endif /* __GAMECARD_IMAGE_DUMP_OPTIONS_FRAME_HPP__ */

View File

@ -0,0 +1,58 @@
/*
* gamecard_image_dump_task_frame.hpp
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __GAMECARD_IMAGE_DUMP_TASK_FRAME_HPP__
#define __GAMECARD_IMAGE_DUMP_TASK_FRAME_HPP__
#include "data_transfer_task_frame.hpp"
#include "../tasks/gamecard_image_dump_task.hpp"
namespace nxdt::views
{
class GameCardImageDumpTaskFrame: public DataTransferTaskFrame<nxdt::tasks::GameCardImageDumpTask>
{
protected:
/* Set class as non-copyable and non-moveable. */
NON_COPYABLE(GameCardImageDumpTaskFrame);
NON_MOVEABLE(GameCardImageDumpTaskFrame);
bool GetTaskResult(std::string& error_msg) override final
{
auto res = this->task.GetResult();
if (res.has_value())
{
error_msg = res.value();
return false;
}
return true;
}
public:
template<typename... Params>
GameCardImageDumpTaskFrame(Params... params) :
DataTransferTaskFrame<nxdt::tasks::GameCardImageDumpTask>(brls::i18n::getStr("gamecard_tab/list/dump_card_image/label"), params...) { }
};
}
#endif /* __GAMECARD_IMAGE_DUMP_TASK_FRAME_HPP__ */

View File

@ -0,0 +1,62 @@
/*
* gamecard_tab.hpp
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __GAMECARD_TAB_HPP__
#define __GAMECARD_TAB_HPP__
#include "root_view.hpp"
#include "layered_error_frame.hpp"
#include "focusable_item.hpp"
namespace nxdt::views
{
class GameCardTab: public LayeredErrorFrame
{
private:
typedef bool (*GameCardSizeFunc)(u64 *out_size);
RootView *root_view = nullptr;
nxdt::tasks::GameCardStatusEvent::Subscription gc_status_task_sub;
GameCardStatus gc_status = GameCardStatus_NotInserted;
std::string raw_filename_full = "";
std::string raw_filename_id_only = "";
void ProcessGameCardStatus(const GameCardStatus& gc_status);
void PopulateList(void);
void AddApplicationMetadataItems(void);
void AddPropertiesTable(void);
void GenerateRawFilenames(void);
std::string GetFormattedSizeString(GameCardSizeFunc func);
std::string GetCardIdSetString(const FsGameCardIdSet& card_id_set);
public:
GameCardTab(RootView *root_view);
~GameCardTab();
};
}
#endif /* __GAMECARD_TAB_HPP__ */

View File

@ -0,0 +1,59 @@
/*
* layered_error_frame.hpp
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __LAYERED_ERROR_FRAME_HPP__
#define __LAYERED_ERROR_FRAME_HPP__
#include "error_frame.hpp"
namespace nxdt::views
{
/* Extended class to switch between ErrorFrame and List views on demand. */
/* Intended to be used with lists that need to be updated dynamically based on runtime events. */
/* Implements some hacky workarounds to prevent borealis from crashing and/or going out of focus. */
class LayeredErrorFrame: public brls::LayerView
{
private:
brls::SidebarItem *sidebar_item = nullptr;
protected:
ErrorFrame *error_frame = nullptr;
brls::List *list = nullptr;
bool IsListItemFocused(void);
int GetFocusStackViewIndex(void);
bool UpdateFocusStackViewAtIndex(int index, brls::View *view);
brls::View *GetListFirstFocusableChild(void);
void SwitchLayerView(bool use_error_frame, bool update_focused_view = false, bool update_focus_stack = true);
public:
LayeredErrorFrame(std::string msg = "");
void SetParentSidebarItem(brls::SidebarItem *sidebar_item);
};
}
#endif /* __LAYERED_ERROR_FRAME_HPP__ */

View File

@ -0,0 +1,80 @@
/*
* options_tab.hpp
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __OPTIONS_TAB_HPP__
#define __OPTIONS_TAB_HPP__
#include "root_view.hpp"
#include "data_transfer_progress_display.hpp"
#include "../tasks/download_file_task.hpp"
#include "../tasks/download_data_task.hpp"
namespace nxdt::views
{
/* Update application frame. */
class OptionsTabUpdateApplicationFrame: public brls::StagedAppletFrame
{
private:
nxdt::tasks::DownloadDataTask json_task;
char *json_buf = nullptr;
size_t json_buf_size = 0;
UtilsGitHubReleaseJsonData json_data = {0};
brls::Label *wait_lbl = nullptr; /// First stage.
brls::List *changelog_list = nullptr; /// Second stage.
DataTransferProgressDisplay *update_progress = nullptr; /// Third stage.
nxdt::tasks::DownloadFileTask nro_task;
void DisplayChangelog(void);
void DisplayUpdateProgress(void);
protected:
void layout(NVGcontext* vg, brls::Style* style, brls::FontStash* stash) override;
bool onCancel(void) override;
public:
OptionsTabUpdateApplicationFrame();
~OptionsTabUpdateApplicationFrame();
};
class OptionsTab: public brls::List
{
private:
RootView *root_view = nullptr;
nxdt::tasks::UmsDeviceVector ums_devices{};
nxdt::tasks::UmsEvent::Subscription ums_task_sub;
bool display_notification = true;
brls::menu_timer_t notification_timer = 0.0f;
brls::menu_timer_ctx_entry_t notification_timer_ctx{};
void DisplayNotification(const std::string& str);
public:
OptionsTab(RootView *root_view);
~OptionsTab();
};
}
#endif /* __OPTIONS_TAB_HPP__ */

123
include/views/root_view.hpp Normal file
View File

@ -0,0 +1,123 @@
/*
* root_view.hpp
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __ROOT_VIEW_HPP__
#define __ROOT_VIEW_HPP__
#include "../tasks/status_info_task.hpp"
#include "../tasks/gamecard_status_task.hpp"
#include "../tasks/title_metadata_task.hpp"
#include "../tasks/ums_task.hpp"
#include "../tasks/usb_host_task.hpp"
#define EVENT_SUBSCRIPTION(func_name, event_type, task_name) \
ALWAYS_INLINE nxdt::tasks::event_type::Subscription Register##func_name##Listener(nxdt::tasks::event_type::Callback cb) { return this->task_name->RegisterListener(cb); } \
ALWAYS_INLINE void Unregister##func_name##Listener(nxdt::tasks::event_type::Subscription subscription) { this->task_name->UnregisterListener(subscription); }
namespace nxdt::views
{
class RootView: public brls::TabFrame
{
private:
bool applet_mode = false;
int output_storage = ConfigOutputStorage_SdCard;
brls::Label *applet_mode_lbl = nullptr;
brls::Label *time_lbl = nullptr;
brls::Label *battery_icon = nullptr, *battery_percentage = nullptr;
brls::Label *connection_icon = nullptr, *connection_status_lbl = nullptr;
brls::Label *usb_icon = nullptr, *ums_counter_lbl = nullptr;
brls::Label *cable_icon = nullptr, *usb_host_speed_lbl = nullptr;
nxdt::tasks::StatusInfoTask *status_info_task = nullptr;
nxdt::tasks::GameCardStatusTask *gc_status_task = nullptr;
nxdt::tasks::TitleMetadataTask *title_metadata_task = nullptr;
nxdt::tasks::UmsTask *ums_task = nullptr;
nxdt::tasks::UsbHostTask *usb_host_task = nullptr;
nxdt::tasks::StatusInfoEvent::Subscription status_info_task_sub;
nxdt::tasks::UmsEvent::Subscription ums_task_sub;
nxdt::tasks::UsbHostEvent::Subscription usb_host_task_sub;
protected:
void draw(NVGcontext* vg, int x, int y, unsigned width, unsigned height, brls::Style* style, brls::FrameContext* ctx) override;
void layout(NVGcontext* vg, brls::Style* style, brls::FontStash* stash) override;
brls::View *getDefaultFocus(void) override;
public:
RootView();
~RootView();
static std::string GetFormattedDateString(const struct tm& timeinfo);
/* Helpers used to propagate the selected output storage throughout different parts of the UI. */
ALWAYS_INLINE int GetOutputStorage(void)
{
return this->output_storage;
}
ALWAYS_INLINE void SetOutputStorage(int value)
{
this->output_storage = value;
}
/* Wrappers for task methods. */
ALWAYS_INLINE bool IsInternetConnectionAvailable(void)
{
return this->status_info_task->IsInternetConnectionAvailable();
}
ALWAYS_INLINE const GameCardStatus& GetGameCardStatus(void)
{
return this->gc_status_task->GetGameCardStatus();
}
ALWAYS_INLINE const nxdt::tasks::TitleApplicationMetadataInfo& GetApplicationMetadataInfo(bool is_system)
{
return this->title_metadata_task->GetApplicationMetadataInfo(is_system);
}
ALWAYS_INLINE const nxdt::tasks::UmsDeviceVector& GetUmsDevices(void)
{
return this->ums_task->GetUmsDevices();
}
ALWAYS_INLINE const UsbHostSpeed& GetUsbHostSpeed(void)
{
return this->usb_host_task->GetUsbHostSpeed();
}
EVENT_SUBSCRIPTION(StatusInfoTask, StatusInfoEvent, status_info_task);
EVENT_SUBSCRIPTION(GameCardStatusTask, GameCardStatusEvent, gc_status_task);
EVENT_SUBSCRIPTION(TitleMetadataTask, UserTitleEvent, title_metadata_task);
EVENT_SUBSCRIPTION(UmsTask, UmsEvent, ums_task);
EVENT_SUBSCRIPTION(UsbHostTask, UsbHostEvent, usb_host_task);
};
}
#undef EVENT_SUBSCRIPTION
#endif /* __ROOT_VIEW_HPP__ */

View File

@ -0,0 +1,87 @@
/*
* titles_tab.hpp
*
* Copyright (c) 2020-2024, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool).
*
* nxdumptool is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* nxdumptool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __TITLES_TAB_HPP__
#define __TITLES_TAB_HPP__
#include "root_view.hpp"
#include "layered_error_frame.hpp"
namespace nxdt::views
{
/* Expanded TabFrame class used as a PopupFrame for titles. */
class TitlesTabPopup: public brls::TabFrame
{
private:
const TitleApplicationMetadata *app_metadata = nullptr;
bool is_system = false;
TitleUserApplicationData user_app_data{};
TitleInfo *system_title_info = nullptr;
public:
TitlesTabPopup(const TitleApplicationMetadata *app_metadata, bool is_system);
~TitlesTabPopup();
};
/* Expanded ListItem class to hold application metadata. */
class TitlesTabItem: public brls::ListItem
{
private:
const TitleApplicationMetadata *app_metadata = nullptr;
bool is_system = false;
bool click_anim = true;
public:
TitlesTabItem(const TitleApplicationMetadata *app_metadata, bool is_system, bool click_anim = true);
void playClickAnimation(void) override;
ALWAYS_INLINE const TitleApplicationMetadata *GetApplicationMetadata(void)
{
return this->app_metadata;
}
ALWAYS_INLINE bool IsSystemTitle(void)
{
return this->is_system;
}
};
class TitlesTab: public LayeredErrorFrame
{
private:
RootView *root_view = nullptr;
nxdt::tasks::UserTitleEvent::Subscription title_task_sub;
bool is_system = false;
void PopulateList(const nxdt::tasks::TitleApplicationMetadataInfo& app_metadata_info);
public:
TitlesTab(RootView *root_view, bool is_system);
~TitlesTab();
};
}
#endif /* __TITLES_TAB_HPP__ */

1
libs/borealis Submodule

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

1
libs/libusbhsfs Submodule

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 629 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 434 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 637 B

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