Initial NTM sources

This commit is contained in:
Edoardo Lolletti 2024-04-25 19:13:10 +02:00
commit 35b8982442
51 changed files with 13413 additions and 0 deletions

57
.github/workflows/build.yml vendored Normal file
View File

@ -0,0 +1,57 @@
name: Build NAND Title Manager
on:
push:
branches: ["*"]
paths-ignore:
- 'README.md'
pull_request:
branches: ["*"]
paths-ignore:
- 'README.md'
release:
types: [created]
jobs:
build:
runs-on: ubuntu-latest
container: devkitpro/devkitarm
name: Build with Docker using devkitARM
steps:
- name: Checkout repo
uses: actions/checkout@v1
- name: Setup environment
run: git config --global safe.directory '*'
- name: Build NAND Title Manager
run: make
- name: Publish build to GH Actions
uses: actions/upload-artifact@v2
with:
path: "*.dsi"
name: build
# Only run this for non-PR jobs.
publish_build:
runs-on: ubuntu-latest
name: Upload to release
if: ${{ success() && startsWith(github.ref, 'refs/tags') }}
needs: build
steps:
- name: Download artifacts
uses: actions/download-artifact@v2
with:
name: build
path: build
- name:
if:
run: |
ID=$(jq --raw-output '.release.id' $GITHUB_EVENT_PATH)
for file in ${{ github.workspace }}/build/*; do
AUTH_HEADER="Authorization: token ${{ secrets.GITHUB_TOKEN }}"
CONTENT_LENGTH="Content-Length: $(stat -c%s $file)"
CONTENT_TYPE="Content-Type: application/7z-x-compressed"
UPLOAD_URL="https://uploads.github.com/repos/${{ github.repository }}/releases/$ID/assets?name=$(basename $file)"
curl -XPOST -H "$AUTH_HEADER" -H "$CONTENT_LENGTH" -H "$CONTENT_TYPE" --upload-file "$file" "$UPLOAD_URL"
done

12
.gitignore vendored Normal file
View File

@ -0,0 +1,12 @@
*.o
*.d
*.nds
*.dsi
*.elf
*.map
*build/
*.vscode
*.DS_Store
*._*
arm9/include/version.h

674
LICENSE.txt Normal file
View File

@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
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.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
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
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 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 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.
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.
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.
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.
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.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"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.
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.
A "covered work" means either the unmodified Program or a work based
on the Program.
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.
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.
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.
1. Source Code.
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 "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.
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.
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 Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
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.
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.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
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.
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.
4. Conveying Verbatim Copies.
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.
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.
5. Conveying Modified Source Versions.
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:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
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".
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.
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.
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.
6. Conveying Non-Source Forms.
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:
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.
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.
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 does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<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, 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 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/>.
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>.

69
Makefile Normal file
View File

@ -0,0 +1,69 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
endif
export TARGET := $(shell basename $(CURDIR))
export TOPDIR := $(CURDIR)
# specify a directory which contains the nitro filesystem
# this is relative to the Makefile
NITRO_FILES :=
# These set the information text in the nds file
GAME_TITLE := NAND Title Manager
GAME_SUBTITLE1 := JeffRuLz, Pk11
GAME_CODE := HTMA
GAME_LABEL := NANDTM
include $(DEVKITARM)/ds_rules
icons := $(wildcard *.bmp)
ifneq (,$(findstring $(TARGET).bmp,$(icons)))
export GAME_ICON := $(CURDIR)/$(TARGET).bmp
else
ifneq (,$(findstring icon.bmp,$(icons)))
export GAME_ICON := $(CURDIR)/icon.bmp
endif
endif
.PHONY: checkarm7 checkarm9 clean
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
all: checkarm7 checkarm9 $(TARGET).dsi
#---------------------------------------------------------------------------------
checkarm7:
$(MAKE) -C arm7
#---------------------------------------------------------------------------------
checkarm9:
$(MAKE) -C arm9
#---------------------------------------------------------------------------------
$(TARGET).dsi : $(NITRO_FILES) arm7/$(TARGET).elf arm9/$(TARGET).elf
ndstool -c $(TARGET).dsi -7 arm7/$(TARGET).elf -9 arm9/$(TARGET).elf \
-u "00030004" \
-g "$(GAME_CODE)" "00" "$(GAME_LABEL)" \
-b $(GAME_ICON) "$(GAME_TITLE);$(GAME_SUBTITLE1)" \
$(_ADDFILES)
#---------------------------------------------------------------------------------
arm7/$(TARGET).elf:
$(MAKE) -C arm7
#---------------------------------------------------------------------------------
arm9/$(TARGET).elf:
$(MAKE) -C arm9
#---------------------------------------------------------------------------------
clean:
$(MAKE) -C arm9 clean
$(MAKE) -C arm7 clean
rm -f $(TARGET).dsi $(TARGET).arm7 $(TARGET).arm9

33
README.md Normal file
View File

@ -0,0 +1,33 @@
# NAND Title Manager
A basic title manager for the Nintendo DSi supporting both hiyaCFW's SDNAND and SysNAND, modified from JeffRuLz's Title Manager for HiyaCFW.
## WARNING
This can modify your internal system NAND! There is *always* a risk of **bricking**, albeit small, when you modify NAND. Please proceed with caution. Having Unlaunch installed is also strongly recommended as it will likely protect you if something manages to go wrong.
## Features
- Install DSiWare and homebrew onto your hiyaCFW SDNAND and SysNAND DSi Menus
- Delete system titles and others hidden from Data Management
- Backup and restore installed titles
- View basic title header info
## Notes
- Backup your SD card and your NAND! Nothing bad should happen, but this is an early release so who knows
- This cannot install cartridge games or older DS homebrew directly, for those you need to make [forwarders](https://wiki.ds-homebrew.com/ds-index/forwarders)
- Always test your forwarders from TWiLight Menu++ or Unlaunch before installing to SysNAND
- Save files and legit TMDs can be used by giving them the following names, where `[rom name]` is the file name of the ROM *without* the extension
- `public.sav` => `[rom name].pub`
- `private.sav` => `[rom name].prv`
- `banner.sav` => `[rom name].bnr`
- `title.tmd` => `[rom name].tmd`
- If you want your DSiWare to work without RSA patches make sure to provide a legit TMD
- Homebrew and DSiWare without a legit TMD require Unlaunch installed with its launcher patches enabled when installed to SysNAND
- Out of region DSiWare cannot be used from SysNAND without Unlaunch's launcher patches
- This is only for DSi systems, not 3DS or DS
## Credits
- [DevkitPro](https://devkitpro.org/): devkitARM and libnds
- [Tuxality](https://github.com/Tuxality): [maketmd](https://github.com/Tuxality/maketmd)
- [Martin Korth (nocash)](https://problemkaputt.de): [GBATEK](https://problemkaputt.de/gbatek.htm)
- [JeffRuLz](https://github.com/JeffRuLz): [TMFH](https://github.com/JeffRuLz/TMFH) (what this is a fork of)
- [DesperateProgrammer](https://github.com/DesperateProgrammer): [DSi Language Patcher](https://github.com/DesperateProgrammer/DSiLanguagePacher) (working NAND writing code)
- [rvtr](https://github.com/rvtr): Adding support for installing dev titles

127
arm7/Makefile Normal file
View File

@ -0,0 +1,127 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
endif
include $(DEVKITARM)/ds_rules
#---------------------------------------------------------------------------------
# BUILD is the directory where object files & intermediate files will be placed
# SOURCES is a list of directories containing source code
# INCLUDES is a list of directories containing extra header files
# DATA is a list of directories containing binary files
# all directories are relative to this makefile
#---------------------------------------------------------------------------------
BUILD := build
SOURCES := src
INCLUDES := include build
DATA :=
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -mthumb-interwork
CFLAGS := -g -Wall -O2\
-mcpu=arm7tdmi -mtune=arm7tdmi -fomit-frame-pointer\
-ffast-math \
$(ARCH)
CFLAGS += $(INCLUDE) -DARM7
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -fno-rtti
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=ds_arm7.specs -g $(ARCH) -Wl,--nmagic -Wl,-Map,$(notdir $*).map
#LIBS := -ldswifi7 -lmm7 -lnds7
LIBS := -lnds7
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS := $(LIBNDS)
#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#---------------------------------------------------------------------------------
ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------
export ARM7ELF := $(CURDIR)/$(TARGET).elf
export DEPSDIR := $(CURDIR)/$(BUILD)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir))
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
export OFILES := $(addsuffix .o,$(BINFILES)) \
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
-I$(CURDIR)/$(BUILD)
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
#---------------------------------------------------------------------------------
export LD := $(CC)
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
export LD := $(CXX)
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------
.PHONY: $(BUILD) clean
#---------------------------------------------------------------------------------
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) *.elf
#---------------------------------------------------------------------------------
else
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
$(ARM7ELF) : $(OFILES)
@echo linking $(notdir $@)
@$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

209
arm7/src/main.c Normal file
View File

@ -0,0 +1,209 @@
/*---------------------------------------------------------------------------------
default ARM7 core
Copyright (C) 2005 - 2010
Michael Noland (joat)
Jason Rogers (dovoto)
Dave Murphy (WinterMute)
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you
must not claim that you wrote the original software. If you use
this software in a product, an acknowledgment in the product
documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
---------------------------------------------------------------------------------*/
#include "my_sdmmc.h"
#include <nds.h>
#include <string.h>
//---------------------------------------------------------------------------------
void VcountHandler()
//---------------------------------------------------------------------------------
{
inputGetAndSend();
}
volatile bool exitflag = false;
volatile bool reboot = false;
// Custom POWER button handling, based on the default function:
// https://github.com/devkitPro/libnds/blob/154a21cc3d57716f773ff2b10f815511c1b8ba9f/source/common/interrupts.c#L51-L69
//---------------------------------------------------------------------------------
TWL_CODE void i2cIRQHandlerCustom()
//---------------------------------------------------------------------------------
{
int cause = (i2cReadRegister(I2C_PM, I2CREGPM_PWRIF) & 0x3) | (i2cReadRegister(I2C_GPIO, 0x02)<<2);
switch (cause & 3)
{
case 1:
reboot = true;
exitflag = true;
break;
case 2:
reboot = false;
exitflag = true;
break;
}
}
void set_ctr(u32* ctr)
{
for (int i = 0; i < 4; i++) REG_AES_IV[i] = ctr[3-i];
}
// 10 11 22 23 24 25
//this is sort of a bodged together dsi aes function adapted from this 3ds function
//https://github.com/TiniVi/AHPCFW/blob/master/source/aes.c#L42
//as long as the output changes when keyslot values change, it's good enough.
void aes(void* in, void* out, void* iv, u32 method)
{
REG_AES_CNT = ( AES_CNT_MODE(method) |
AES_WRFIFO_FLUSH |
AES_RDFIFO_FLUSH |
AES_CNT_KEY_APPLY |
AES_CNT_KEYSLOT(3) |
AES_CNT_DMA_WRITE_SIZE(2) |
AES_CNT_DMA_READ_SIZE(1)
);
if (iv != NULL) set_ctr((u32*)iv);
REG_AES_BLKCNT = (1 << 16);
REG_AES_CNT |= 0x80000000;
for (int j = 0; j < 0x10; j+=4) REG_AES_WRFIFO = *((u32*)(in+j));
while (((REG_AES_CNT >> 0x5) & 0x1F) < 0x4); //wait for every word to get processed
for (int j = 0; j < 0x10; j+=4) *((u32*)(out+j)) = REG_AES_RDFIFO;
//REG_AES_CNT &= ~0x80000000;
//if (method & (AES_CTR_DECRYPT | AES_CTR_ENCRYPT)) add_ctr((u8*)iv);
}
int my_sdmmc_nand_startup();
//---------------------------------------------------------------------------------
int main()
//---------------------------------------------------------------------------------
{
// clear sound registers
dmaFillWords(0, (void*)0x04000400, 0x100);
REG_SOUNDCNT |= SOUND_ENABLE;
writePowerManagement(PM_CONTROL_REG, ( readPowerManagement(PM_CONTROL_REG) & ~PM_SOUND_MUTE ) | PM_SOUND_AMP );
powerOn(POWER_SOUND);
readUserSettings();
ledBlink(0);
irqInit();
irqSetAUX(IRQ_I2C, i2cIRQHandlerCustom);
// Start the RTC tracking IRQ
initClockIRQ();
fifoInit();
touchInit();
if (isDSiMode() /*|| ((REG_SCFG_EXT & BIT(17)) && (REG_SCFG_EXT & BIT(18)))*/)
{
u8 *out=(u8*)0x02300000;
#if USENATIVECONSOLEID
// first check whether we can read the console ID directly and it was not hidden by SCFG
if (((*(volatile uint16_t *)0x04004000) & (1u << 10)) == 0)
{
// The console id registers are readable, so use them!
memcpy(out, (uint8_t *)0x04004D00, 8);
} else
#endif
{
// For getting ConsoleID without reading from 0x4004D00...
u8 base[16]={0};
u8 in[16]={0};
u8 iv[16]={0};
u8 *scratch=(u8*)0x02300200;
u8 *key3=(u8*)0x40044D0;
aes(in, base, iv, 2);
//write consecutive 0-255 values to any byte in key3 until we get the same aes output as "base" above - this reveals the hidden byte. this way we can uncover all 16 bytes of the key3 normalkey pretty easily.
//greets to Martin Korth for this trick https://problemkaputt.de/gbatek.htm#dsiaesioports (Reading Write-Only Values)
for (int i=0;i<16;i++)
{
for (int j=0;j<256;j++)
{
*(key3+i)=j & 0xFF;
aes(in, scratch, iv, 2);
if (!memcmp(scratch, base, 16))
{
out[i]=j;
//hit++;
break;
}
}
}
}
my_sdmmc_nand_startup();
my_sdmmc_get_cid(true, (u32*)0x2FFD7BC); // Get eMMC CID
//sdmmc_nand_cid((u32*)0x2FFD7BC);
}
SetYtrigger(80);
installSoundFIFO();
installSystemFIFO();
irqSet(IRQ_VCOUNT, VcountHandler);
irqEnable( IRQ_VBLANK | IRQ_VCOUNT | IRQ_NETWORK);
// Keep the ARM7 mostly idle
while (!exitflag)
{
if ( 0 == (REG_KEYINPUT & (KEY_SELECT | KEY_START | KEY_L | KEY_R)))
{
exitflag = true;
}
int batteryStatus;
if (isDSiMode() || REG_SCFG_EXT != 0)
batteryStatus = i2cReadRegister(I2C_PM, I2CREGPM_BATTERY);
else
batteryStatus = (readPowerManagement(PM_BATTERY_REG) & 1) ? 0x3 : 0xF;
fifoSendValue32(FIFO_USER_03, batteryStatus);
swiWaitForVBlank();
}
// Tell ARM9 to safely exit
fifoSendValue32(FIFO_USER_01, 0x54495845); // 'EXIT'
fifoWaitValue32(FIFO_USER_02);
fifoCheckValue32(FIFO_USER_02);
if (reboot)
{
i2cWriteRegister(I2C_PM, I2CREGPM_RESETFLAG, 1);
i2cWriteRegister(I2C_PM, I2CREGPM_PWRCNT, 1);
}
else
{
writePowerManagement(PM_CONTROL_REG,PM_SYSTEM_PWR);
}
return 0;
}

696
arm7/src/my_sdmmc.c Normal file
View File

@ -0,0 +1,696 @@
#include <nds/system.h>
#include <nds/bios.h>
#include "my_sdmmc.h"
#include <nds/interrupts.h>
#include <nds/fifocommon.h>
#include <nds/fifomessages.h>
#include <stddef.h>
static struct mmcdevice deviceSD;
static struct mmcdevice deviceNAND;
/*mmcdevice *getMMCDevice(int drive)
{
if (drive==0) return &deviceNAND;
return &deviceSD;
}
*/
//---------------------------------------------------------------------------------
int my_geterror(struct mmcdevice *ctx)
//---------------------------------------------------------------------------------
{
//if (ctx->error == 0x4) return -1;
//else return 0;
return (ctx->error << 29) >> 31;
}
//---------------------------------------------------------------------------------
void my_setTarget(struct mmcdevice *ctx)
//---------------------------------------------------------------------------------
{
sdmmc_mask16(REG_SDPORTSEL,0x3,(u16)ctx->devicenumber);
setckl(ctx->clk);
if (ctx->SDOPT == 0)
{
sdmmc_mask16(REG_SDOPT, 0, 0x8000);
}
else
{
sdmmc_mask16(REG_SDOPT, 0x8000, 0);
}
}
//---------------------------------------------------------------------------------
void my_sdmmc_send_command(struct mmcdevice *ctx, uint32_t cmd, uint32_t args)
//---------------------------------------------------------------------------------
{
const bool getSDRESP = (cmd << 15) >> 31;
u16 flags = (cmd << 15) >> 31;
const bool readdata = cmd & 0x20000;
const bool writedata = cmd & 0x40000;
if (readdata || writedata)
{
flags |= TMIO_STAT0_DATAEND;
}
ctx->error = 0;
while ((sdmmc_read16(REG_SDSTATUS1) & TMIO_STAT1_CMD_BUSY)); //mmc working?
sdmmc_write16(REG_SDIRMASK0,0);
sdmmc_write16(REG_SDIRMASK1,0);
sdmmc_write16(REG_SDSTATUS0,0);
sdmmc_write16(REG_SDSTATUS1,0);
sdmmc_mask16(REG_SDDATACTL32,0x1800,0x400); // Disable TX32RQ and RX32RDY IRQ. Clear fifo.
sdmmc_write16(REG_SDCMDARG0,args &0xFFFF);
sdmmc_write16(REG_SDCMDARG1,args >> 16);
sdmmc_write16(REG_SDCMD,cmd &0xFFFF);
u32 size = ctx->size;
const u16 blkSize = sdmmc_read16(REG_SDBLKLEN32);
#ifdef DATA32_SUPPORT
u32 *rDataPtr32 = (u32*)ctx->rData;
#else
u16 *rDataPtr16 = (u16*)ctx->rData;
#endif
u8 *rDataPtr8 = ctx->rData;
#ifdef DATA32_SUPPORT
const u32 *tDataPtr32 = (u32*)ctx->tData;
#else
const u16 *tDataPtr16 = (u16*)ctx->tData;
#endif
const u8 *tDataPtr8 = ctx->tData;
#ifdef DATA32_SUPPORT
bool rUseBuf = ( NULL != rDataPtr32 );
bool tUseBuf = ( NULL != tDataPtr32 );
#else
bool rUseBuf = ( NULL != rDataPtr16 );
bool tUseBuf = ( NULL != tDataPtr16 );
#endif
u16 status0 = 0;
while (1)
{
volatile u16 status1 = sdmmc_read16(REG_SDSTATUS1);
#ifdef DATA32_SUPPORT
volatile u16 ctl32 = sdmmc_read16(REG_SDDATACTL32);
if (ctl32 & 0x100)
#else
if (status1 & TMIO_STAT1_RXRDY)
#endif
{
if (readdata)
{
if (rUseBuf)
{
sdmmc_mask16(REG_SDSTATUS1, TMIO_STAT1_RXRDY, 0);
if (size >= blkSize)
{
#ifdef DATA32_SUPPORT
if (!((u32)rDataPtr32 & 3))
{
for (u32 i = 0; i < blkSize; i += 4)
{
*rDataPtr32++ = sdmmc_read32(REG_SDFIFO32);
}
}
else
{
for (u32 i = 0; i < blkSize; i += 4)
{
u32 data = sdmmc_read32(REG_SDFIFO32);
*rDataPtr8++ = data;
*rDataPtr8++ = data >> 8;
*rDataPtr8++ = data >> 16;
*rDataPtr8++ = data >> 24;
}
}
#else
if (!((u16)rDataPtr16 & 1))
{
for (u16 i = 0; i < blkSize; i += 2)
{
*rDataPtr16++ = sdmmc_read16(REG_SDFIFO);
}
}
else
{
for (u16 i = 0; i < blkSize; i += 2)
{
u16 data = sdmmc_read16(REG_SDFIFO);
*rDataPtr8++ = data;
*rDataPtr8++ = data >> 8;
}
}
#endif
size -= blkSize;
}
}
sdmmc_mask16(REG_SDDATACTL32, 0x800, 0);
}
}
#ifdef DATA32_SUPPORT
if (!(ctl32 & 0x200))
#else
if ((status1 & TMIO_STAT1_TXRQ))
#endif
{
if (writedata)
{
if (tUseBuf)
{
sdmmc_mask16(REG_SDSTATUS1, TMIO_STAT1_TXRQ, 0);
if (size >= blkSize)
{
#ifdef DATA32_SUPPORT
if (!((u32)tDataPtr32 & 3))
{
for (u32 i = 0; i < blkSize; i += 4)
{
sdmmc_write32(REG_SDFIFO32, *tDataPtr32++);
}
}
else
{
for (u32 i = 0; i < blkSize; i += 4)
{
u32 data = *tDataPtr8++;
data |= (u32)*tDataPtr8++ << 8;
data |= (u32)*tDataPtr8++ << 16;
data |= (u32)*tDataPtr8++ << 24;
sdmmc_write32(REG_SDFIFO32, data);
}
}
#else
if (!((u16)tDataPtr16 & 1))
{
for (u16 i = 0; i < blkSize; i += 2)
{
sdmmc_write16(REG_SDFIFO, *tDataPtr16++);
}
}
else
{
for (u16 i = 0; i < blkSize; i += 2)
{
u16 data = *tDataPtr8++;
data |= (u16)(*tDataPtr8++ << 8);
sdmmc_write16(REG_SDFIFO, data);
}
}
#endif
size -= blkSize;
}
}
sdmmc_mask16(REG_SDDATACTL32, 0x1000, 0);
}
}
if (status1 & TMIO_MASK_GW)
{
ctx->error |= 4;
break;
}
if (!(status1 & TMIO_STAT1_CMD_BUSY))
{
status0 = sdmmc_read16(REG_SDSTATUS0);
if (sdmmc_read16(REG_SDSTATUS0) & TMIO_STAT0_CMDRESPEND)
{
ctx->error |= 0x1;
}
if (status0 & TMIO_STAT0_DATAEND)
{
ctx->error |= 0x2;
}
if ((status0 & flags) == flags)
break;
}
}
ctx->stat0 = sdmmc_read16(REG_SDSTATUS0);
ctx->stat1 = sdmmc_read16(REG_SDSTATUS1);
sdmmc_write16(REG_SDSTATUS0,0);
sdmmc_write16(REG_SDSTATUS1,0);
if (getSDRESP != 0)
{
ctx->ret[0] = (u32)(sdmmc_read16(REG_SDRESP0) | (sdmmc_read16(REG_SDRESP1) << 16));
ctx->ret[1] = (u32)(sdmmc_read16(REG_SDRESP2) | (sdmmc_read16(REG_SDRESP3) << 16));
ctx->ret[2] = (u32)(sdmmc_read16(REG_SDRESP4) | (sdmmc_read16(REG_SDRESP5) << 16));
ctx->ret[3] = (u32)(sdmmc_read16(REG_SDRESP6) | (sdmmc_read16(REG_SDRESP7) << 16));
}
}
//---------------------------------------------------------------------------------
int my_sdmmc_cardinserted()
//---------------------------------------------------------------------------------
{
return 1; //my_sdmmc_cardready;
}
static bool my_sdmmc_controller_initialised = false;
//---------------------------------------------------------------------------------
void my_sdmmc_controller_init( bool force_init )
//---------------------------------------------------------------------------------
{
if (!force_init && my_sdmmc_controller_initialised) return;
deviceSD.isSDHC = 0;
deviceSD.SDOPT = 0;
deviceSD.res = 0;
deviceSD.initarg = 0;
deviceSD.clk = 0x80;
deviceSD.devicenumber = 0;
deviceNAND.isSDHC = 0;
deviceNAND.SDOPT = 0;
deviceNAND.res = 0;
deviceNAND.initarg = 1;
deviceNAND.clk = 0x80;
deviceNAND.devicenumber = 1;
*(vu16*)(SDMMC_BASE + REG_SDDATACTL32) &= 0xF7FFu;
*(vu16*)(SDMMC_BASE + REG_SDDATACTL32) &= 0xEFFFu;
#ifdef DATA32_SUPPORT
*(vu16*)(SDMMC_BASE + REG_SDDATACTL32) |= 0x402u;
#else
*(vu16*)(SDMMC_BASE + REG_SDDATACTL32) |= 0x402u;
#endif
*(vu16*)(SDMMC_BASE + REG_SDDATACTL) = (*(vu16*)(SDMMC_BASE + REG_SDDATACTL) & 0xFFDD) | 2;
#ifdef DATA32_SUPPORT
*(vu16*)(SDMMC_BASE + REG_SDDATACTL32) &= 0xFFFFu;
*(vu16*)(SDMMC_BASE + REG_SDDATACTL) &= 0xFFDFu;
*(vu16*)(SDMMC_BASE + REG_SDBLKLEN32) = 512;
#else
*(vu16*)(SDMMC_BASE + REG_SDDATACTL32) &= 0xFFFDu;
*(vu16*)(SDMMC_BASE + REG_SDDATACTL) &= 0xFFDDu;
*(vu16*)(SDMMC_BASE + REG_SDBLKLEN32) = 0;
#endif
*(vu16*)(SDMMC_BASE + REG_SDBLKCOUNT32) = 1;
*(vu16*)(SDMMC_BASE + REG_SDRESET) &= 0xFFFEu;
*(vu16*)(SDMMC_BASE + REG_SDRESET) |= 1u;
*(vu16*)(SDMMC_BASE + REG_SDIRMASK0) |= TMIO_MASK_ALL;
*(vu16*)(SDMMC_BASE + REG_SDIRMASK1) |= TMIO_MASK_ALL>>16;
*(vu16*)(SDMMC_BASE + 0x0fc) |= 0xDBu; //SDCTL_RESERVED7
*(vu16*)(SDMMC_BASE + 0x0fe) |= 0xDBu; //SDCTL_RESERVED8
*(vu16*)(SDMMC_BASE + REG_SDPORTSEL) &= 0xFFFCu;
#ifdef DATA32_SUPPORT
*(vu16*)(SDMMC_BASE + REG_SDCLKCTL) = 0x20;
*(vu16*)(SDMMC_BASE + REG_SDOPT) = 0x40EE;
#else
*(vu16*)(SDMMC_BASE + REG_SDCLKCTL) = 0x40; //Nintendo sets this to 0x20
*(vu16*)(SDMMC_BASE + REG_SDOPT) = 0x40EB; //Nintendo sets this to 0x40EE
#endif
*(vu16*)(SDMMC_BASE + REG_SDPORTSEL) &= 0xFFFCu;
*(vu16*)(SDMMC_BASE + REG_SDBLKLEN) = 512;
*(vu16*)(SDMMC_BASE + REG_SDSTOP) = 0;
my_sdmmc_controller_initialised = true;
my_setTarget(&deviceSD);
}
//---------------------------------------------------------------------------------
static u32 calcSDSize(u8* csd, int type)
//---------------------------------------------------------------------------------
{
u32 result = 0;
if (type == -1) type = csd[14] >> 6;
switch (type)
{
case 0:
{
u32 block_len = csd[9] & 0xf;
block_len = 1 << block_len;
u32 mult = (csd[4] >> 7) | ((csd[5] & 3) << 1);
mult = 1 << (mult + 2);
result = csd[8] & 3;
result = (result << 8) | csd[7];
result = (result << 2) | (csd[6] >> 6);
result = (result + 1) * mult * block_len / 512;
}
break;
case 1:
result = csd[7] & 0x3f;
result = (result << 8) | csd[6];
result = (result << 8) | csd[5];
result = (result + 1) * 1024;
break;
}
return result;
}
//---------------------------------------------------------------------------------
int my_sdmmc_sdcard_init()
//---------------------------------------------------------------------------------
{
// We need to send at least 74 clock pulses.
my_setTarget(&deviceSD);
swiDelay(0x1980); // ~75-76 clocks
// card reset
my_sdmmc_send_command(&deviceSD,0,0);
// CMD8 0x1AA
my_sdmmc_send_command(&deviceSD,0x10408,0x1AA);
u32 temp = (deviceSD.error & 0x1) << 0x1E;
u32 temp2 = 0;
do {
do {
// CMD55
my_sdmmc_send_command(&deviceSD,0x10437,deviceSD.initarg << 0x10);
// ACMD41
my_sdmmc_send_command(&deviceSD,0x10769,0x00FF8000 | temp);
temp2 = 1;
}
while ( !(deviceSD.error & 1) );
}
while ((deviceSD.ret[0] & 0x80000000) == 0);
if (!((deviceSD.ret[0] >> 30) & 1) || !temp)
temp2 = 0;
deviceSD.isSDHC = temp2;
my_sdmmc_send_command(&deviceSD,0x10602,0);
if (deviceSD.error & 0x4) return -1;
my_sdmmc_send_command(&deviceSD,0x10403,0);
if (deviceSD.error & 0x4) return -1;
deviceSD.initarg = deviceSD.ret[0] >> 0x10;
my_sdmmc_send_command(&deviceSD,0x10609,deviceSD.initarg << 0x10);
if (deviceSD.error & 0x4) return -1;
// Command Class 10 support
const bool cmd6Supported = ((u8*)deviceSD.ret)[10] & 0x40;
deviceSD.total_size = calcSDSize((u8*)&deviceSD.ret[0],-1);
setckl(0x201); // 16.756991 MHz
my_sdmmc_send_command(&deviceSD,0x10507,deviceSD.initarg << 0x10);
if (deviceSD.error & 0x4) return -1;
// CMD55
my_sdmmc_send_command(&deviceSD,0x10437,deviceSD.initarg << 0x10);
if (deviceSD.error & 0x4) return -1;
// ACMD42
my_sdmmc_send_command(&deviceSD,0x1076A,0x0);
if (deviceSD.error & 0x4) return -1;
// CMD55
my_sdmmc_send_command(&deviceSD,0x10437,deviceSD.initarg << 0x10);
if (deviceSD.error & 0x4) return -7;
deviceSD.SDOPT = 1;
my_sdmmc_send_command(&deviceSD,0x10446,0x2);
if (deviceSD.error & 0x4) return -8;
sdmmc_mask16(REG_SDOPT, 0x8000, 0); // Switch to 4 bit mode.
// TODO: CMD6 to switch to high speed mode.
if (cmd6Supported)
{
sdmmc_write16(REG_SDSTOP,0);
sdmmc_write16(REG_SDBLKLEN32,64);
sdmmc_write16(REG_SDBLKLEN,64);
deviceSD.rData = NULL;
deviceSD.size = 64;
my_sdmmc_send_command(&deviceSD,0x31C06,0x80FFFFF1);
sdmmc_write16(REG_SDBLKLEN,512);
if (deviceSD.error & 0x4) return -9;
deviceSD.clk = 0x200; // 33.513982 MHz
setckl(0x200);
}
else deviceSD.clk = 0x201; // 16.756991 MHz
my_sdmmc_send_command(&deviceSD,0x1040D,deviceSD.initarg << 0x10);
if (deviceSD.error & 0x4) return -9;
my_sdmmc_send_command(&deviceSD,0x10410,0x200);
if (deviceSD.error & 0x4) return -10;
return 0;
}
//---------------------------------------------------------------------------------
int my_sdmmc_nand_init()
//---------------------------------------------------------------------------------
{
my_setTarget(&deviceNAND);
swiDelay(0xF000);
my_sdmmc_send_command(&deviceNAND,0,0);
do {
do {
my_sdmmc_send_command(&deviceNAND,0x10701,0x100000);
}
while ( !(deviceNAND.error & 1) );
}
while ((deviceNAND.ret[0] & 0x80000000) == 0);
my_sdmmc_send_command(&deviceNAND,0x10602,0x0);
if ((deviceNAND.error & 0x4))return -1;
my_sdmmc_send_command(&deviceNAND,0x10403,deviceNAND.initarg << 0x10);
if ((deviceNAND.error & 0x4))return -1;
my_sdmmc_send_command(&deviceNAND,0x10609,deviceNAND.initarg << 0x10);
if ((deviceNAND.error & 0x4))return -1;
deviceNAND.total_size = calcSDSize((uint8_t*)&deviceNAND.ret[0],0);
deviceNAND.clk = 1;
setckl(1);
my_sdmmc_send_command(&deviceNAND,0x10407,deviceNAND.initarg << 0x10);
if ((deviceNAND.error & 0x4))return -1;
deviceNAND.SDOPT = 1;
my_sdmmc_send_command(&deviceNAND,0x10506,0x3B70100);
if ((deviceNAND.error & 0x4))return -1;
my_sdmmc_send_command(&deviceNAND,0x10506,0x3B90100);
if ((deviceNAND.error & 0x4))return -1;
my_sdmmc_send_command(&deviceNAND,0x1040D,deviceNAND.initarg << 0x10);
if ((deviceNAND.error & 0x4))return -1;
my_sdmmc_send_command(&deviceNAND,0x10410,0x200);
if ((deviceNAND.error & 0x4))return -1;
deviceNAND.clk |= 0x200;
my_setTarget(&deviceSD);
return 0;
}
//---------------------------------------------------------------------------------
int my_sdmmc_readsectors(struct mmcdevice *device, u32 sector_no, u32 numsectors, void *out)
//---------------------------------------------------------------------------------
{
if (device->isSDHC == 0) sector_no <<= 9;
my_setTarget(device);
sdmmc_write16(REG_SDSTOP,0x100);
#ifdef DATA32_SUPPORT
sdmmc_write16(REG_SDBLKCOUNT32,numsectors);
sdmmc_write16(REG_SDBLKLEN32,0x200);
#endif
sdmmc_write16(REG_SDBLKCOUNT,numsectors);
device->rData = out;
device->size = numsectors << 9;
my_sdmmc_send_command(device,0x33C12,sector_no);
my_setTarget(&deviceSD);
return my_geterror(device);
}
//---------------------------------------------------------------------------------
int my_sdmmc_writesectors(struct mmcdevice *device, u32 sector_no, u32 numsectors, void *in)
//---------------------------------------------------------------------------------
{
if (device->isSDHC == 0)
sector_no <<= 9;
my_setTarget(device);
sdmmc_write16(REG_SDSTOP,0x100);
#ifdef DATA32_SUPPORT
sdmmc_write16(REG_SDBLKCOUNT32,numsectors);
sdmmc_write16(REG_SDBLKLEN32,0x200);
#endif
sdmmc_write16(REG_SDBLKCOUNT,numsectors);
device->tData = in;
device->size = numsectors << 9;
my_sdmmc_send_command(device,0x52C19,sector_no);
my_setTarget(&deviceSD);
return my_geterror(device);
}
//---------------------------------------------------------------------------------
void my_sdmmc_get_cid(int devicenumber, u32 *cid)
//---------------------------------------------------------------------------------
{
struct mmcdevice *device = (devicenumber == 1 ? &deviceNAND : &deviceSD);
int oldIME = enterCriticalSection();
my_setTarget(device);
// use cmd7 to put sd card in standby mode
// CMD7
my_sdmmc_send_command(device, 0x10507, 0);
// get sd card info
// use cmd10 to read CID
my_sdmmc_send_command(device, 0x1060A, device->initarg << 0x10);
for (int i = 0; i < 4; ++i)
cid[i] = device->ret[i];
// put sd card back to transfer mode
// CMD7
my_sdmmc_send_command(device, 0x10507, device->initarg << 0x10);
leaveCriticalSection(oldIME);
}
//---------------------------------------------------------------------------------
void my_sdmmcMsgHandler(int bytes, void *user_data)
//---------------------------------------------------------------------------------
{
FifoMessage msg;
int retval = 0;
fifoGetDatamsg(FIFO_SDMMC, bytes, (u8*)&msg);
int oldIME = enterCriticalSection();
switch (msg.type)
{
case SDMMC_SD_READ_SECTORS:
retval = my_sdmmc_readsectors(&deviceSD, msg.sdParams.startsector, msg.sdParams.numsectors, msg.sdParams.buffer);
break;
case SDMMC_SD_WRITE_SECTORS:
retval = my_sdmmc_writesectors(&deviceSD, msg.sdParams.startsector, msg.sdParams.numsectors, msg.sdParams.buffer);
break;
case SDMMC_NAND_READ_SECTORS:
retval = my_sdmmc_readsectors(&deviceNAND, msg.sdParams.startsector, msg.sdParams.numsectors, msg.sdParams.buffer);
break;
case SDMMC_NAND_WRITE_SECTORS:
retval = my_sdmmc_writesectors(&deviceNAND, msg.sdParams.startsector, msg.sdParams.numsectors, msg.sdParams.buffer);
break;
}
leaveCriticalSection(oldIME);
fifoSendValue32(FIFO_SDMMC, retval);
}
//---------------------------------------------------------------------------------
int my_sdmmc_nand_startup()
//---------------------------------------------------------------------------------
{
my_sdmmc_controller_init(false);
return my_sdmmc_nand_init();
}
//---------------------------------------------------------------------------------
int my_sdmmc_sd_startup()
//---------------------------------------------------------------------------------
{
my_sdmmc_controller_init(false);
return my_sdmmc_sdcard_init();
}
//---------------------------------------------------------------------------------
void my_sdmmcValueHandler(u32 value, void* user_data)
//---------------------------------------------------------------------------------
{
int result = 0;
int sdflag = 0;
int oldIME = enterCriticalSection();
switch (value)
{
case SDMMC_HAVE_SD:
result = sdmmc_read16(REG_SDSTATUS0);
break;
case SDMMC_SD_START:
sdflag = 1;
/* Falls through. */
case SDMMC_NAND_START:
if (sdmmc_read16(REG_SDSTATUS0) == 0)
{
result = 1;
}
else
{
result = (sdflag == 1 ) ? my_sdmmc_sd_startup() : my_sdmmc_nand_startup();
}
break;
case SDMMC_SD_IS_INSERTED:
result = my_sdmmc_cardinserted();
break;
case SDMMC_SD_STOP:
break;
case SDMMC_NAND_SIZE:
result = deviceNAND.total_size;
break;
}
leaveCriticalSection(oldIME);
fifoSendValue32(FIFO_SDMMC, result);
}
//---------------------------------------------------------------------------------
int my_sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, void *out)
//---------------------------------------------------------------------------------
{
return my_sdmmc_readsectors(&deviceSD, sector_no, numsectors, out);
}
//---------------------------------------------------------------------------------
int my_sdmmc_sdcard_writesectors(u32 sector_no, u32 numsectors, void *in)
//---------------------------------------------------------------------------------
{
return my_sdmmc_writesectors(&deviceSD, sector_no, numsectors, in);
}
//---------------------------------------------------------------------------------
int my_sdmmc_nand_readsectors(u32 sector_no, u32 numsectors, void *out)
//---------------------------------------------------------------------------------
{
return my_sdmmc_readsectors(&deviceNAND, sector_no, numsectors, out);
}
//---------------------------------------------------------------------------------
int my_sdmmc_nand_writesectors(u32 sector_no, u32 numsectors, void *in)
//---------------------------------------------------------------------------------
{
return my_sdmmc_writesectors(&deviceNAND, sector_no, numsectors, in);
}

205
arm7/src/my_sdmmc.h Normal file
View File

@ -0,0 +1,205 @@
#ifndef __SDMMC_H__
#define __SDMMC_H__
#include <nds/ndstypes.h>
#define DATA32_SUPPORT
#define SDMMC_BASE 0x04004800
#define REG_SDCMD 0x00
#define REG_SDPORTSEL 0x02
#define REG_SDCMDARG 0x04
#define REG_SDCMDARG0 0x04
#define REG_SDCMDARG1 0x06
#define REG_SDSTOP 0x08
#define REG_SDRESP 0x0c
#define REG_SDBLKCOUNT 0x0a
#define REG_SDRESP0 0x0c
#define REG_SDRESP1 0x0e
#define REG_SDRESP2 0x10
#define REG_SDRESP3 0x12
#define REG_SDRESP4 0x14
#define REG_SDRESP5 0x16
#define REG_SDRESP6 0x18
#define REG_SDRESP7 0x1a
#define REG_SDSTATUS0 0x1c
#define REG_SDSTATUS1 0x1e
#define REG_SDIRMASK0 0x20
#define REG_SDIRMASK1 0x22
#define REG_SDCLKCTL 0x24
#define REG_SDBLKLEN 0x26
#define REG_SDOPT 0x28
#define REG_SDFIFO 0x30
#define REG_SDDATACTL 0xd8
#define REG_SDRESET 0xe0
#define REG_SDPROTECTED 0xf6 //bit 0 determines if sd is protected or not?
#define REG_SDDATACTL32 0x100
#define REG_SDBLKLEN32 0x104
#define REG_SDBLKCOUNT32 0x108
#define REG_SDFIFO32 0x10C
#define REG_CLK_AND_WAIT_CTL 0x138
#define REG_RESET_SDIO 0x1e0
//The below defines are from linux kernel drivers/mmc tmio_mmc.h.
/* Definitions for values the CTRL_STATUS register can take. */
#define TMIO_STAT0_CMDRESPEND 0x0001
#define TMIO_STAT0_DATAEND 0x0004
#define TMIO_STAT0_CARD_REMOVE 0x0008
#define TMIO_STAT0_CARD_INSERT 0x0010
#define TMIO_STAT0_SIGSTATE 0x0020
#define TMIO_STAT0_WRPROTECT 0x0080
#define TMIO_STAT0_CARD_REMOVE_A 0x0100
#define TMIO_STAT0_CARD_INSERT_A 0x0200
#define TMIO_STAT0_SIGSTATE_A 0x0400
#define TMIO_STAT1_CMD_IDX_ERR 0x0001
#define TMIO_STAT1_CRCFAIL 0x0002
#define TMIO_STAT1_STOPBIT_ERR 0x0004
#define TMIO_STAT1_DATATIMEOUT 0x0008
#define TMIO_STAT1_RXOVERFLOW 0x0010
#define TMIO_STAT1_TXUNDERRUN 0x0020
#define TMIO_STAT1_CMDTIMEOUT 0x0040
#define TMIO_STAT1_RXRDY 0x0100
#define TMIO_STAT1_TXRQ 0x0200
#define TMIO_STAT1_ILL_FUNC 0x2000
#define TMIO_STAT1_CMD_BUSY 0x4000
#define TMIO_STAT1_ILL_ACCESS 0x8000
#define SDMC_NORMAL 0x00000000
#define SDMC_ERR_COMMAND 0x00000001
#define SDMC_ERR_CRC 0x00000002
#define SDMC_ERR_END 0x00000004
#define SDMC_ERR_TIMEOUT 0x00000008
#define SDMC_ERR_FIFO_OVF 0x00000010
#define SDMC_ERR_FIFO_UDF 0x00000020
#define SDMC_ERR_WP 0x00000040
#define SDMC_ERR_ABORT 0x00000080
#define SDMC_ERR_FPGA_TIMEOUT 0x00000100
#define SDMC_ERR_PARAM 0x00000200
#define SDMC_ERR_R1_STATUS 0x00000800
#define SDMC_ERR_NUM_WR_SECTORS 0x00001000
#define SDMC_ERR_RESET 0x00002000
#define SDMC_ERR_ILA 0x00004000
#define SDMC_ERR_INFO_DETECT 0x00008000
#define SDMC_STAT_ERR_UNKNOWN 0x00080000
#define SDMC_STAT_ERR_CC 0x00100000
#define SDMC_STAT_ERR_ECC_FAILED 0x00200000
#define SDMC_STAT_ERR_CRC 0x00800000
#define SDMC_STAT_ERR_OTHER 0xf9c70008
#define TMIO_MASK_ALL 0x837f031d
#define TMIO_MASK_GW (TMIO_STAT1_ILL_ACCESS | TMIO_STAT1_CMDTIMEOUT | TMIO_STAT1_TXUNDERRUN | TMIO_STAT1_RXOVERFLOW | \
TMIO_STAT1_DATATIMEOUT | TMIO_STAT1_STOPBIT_ERR | TMIO_STAT1_CRCFAIL | TMIO_STAT1_CMD_IDX_ERR)
#define TMIO_MASK_READOP (TMIO_STAT1_RXRDY | TMIO_STAT1_DATAEND)
#define TMIO_MASK_WRITEOP (TMIO_STAT1_TXRQ | TMIO_STAT1_DATAEND)
typedef struct mmcdevice {
u8* rData;
const u8* tData;
u32 size;
u32 startOffset;
u32 endOffset;
u32 error;
u16 stat0;
u16 stat1;
u32 ret[4];
u32 initarg;
u32 isSDHC;
u32 clk;
u32 SDOPT;
u32 devicenumber;
u32 total_size; //size in sectors of the device
u32 res;
} mmcdevice;
enum {
MMC_DEVICE_SDCARD,
MMC_DEVICE_NAND,
};
void my_sdmmc_controller_init(bool force_init);
void my_sdmmc_initirq();
int my_sdmmc_cardinserted();
int my_sdmmc_sdcard_init();
int my_sdmmc_nand_init();
void my_sdmmc_get_cid(int devicenumber, u32 *cid);
static inline void sdmmc_nand_cid( u32 *cid)
{
my_sdmmc_get_cid(MMC_DEVICE_NAND, cid);
}
static inline void sdmmc_sdcard_cid( u32 *cid)
{
my_sdmmc_get_cid(MMC_DEVICE_SDCARD, cid);
}
int my_sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, void *out);
int my_sdmmc_sdcard_writesectors(u32 sector_no, u32 numsectors, void *in);
int my_sdmmc_nand_readsectors(u32 sector_no, u32 numsectors, void *out);
int my_sdmmc_nand_writesectors(u32 sector_no, u32 numsectors, void *in);
extern u32 sdmmc_cid[];
extern int sdmmc_curdevice;
//---------------------------------------------------------------------------------
static inline u16 sdmmc_read16(u16 reg)
//---------------------------------------------------------------------------------
{
return *(vu16*)(SDMMC_BASE + reg);
}
//---------------------------------------------------------------------------------
static inline void sdmmc_write16(u16 reg, u16 val)
//---------------------------------------------------------------------------------
{
*(vu16*)(SDMMC_BASE + reg) = val;
}
//---------------------------------------------------------------------------------
static inline u32 sdmmc_read32(u16 reg)
//---------------------------------------------------------------------------------
{
return *(vu32*)(SDMMC_BASE + reg);
}
//---------------------------------------------------------------------------------
static inline void sdmmc_write32(u16 reg, u32 val)
//---------------------------------------------------------------------------------
{
*(vu32*)(SDMMC_BASE + reg) = val;
}
//---------------------------------------------------------------------------------
static inline void sdmmc_mask16(u16 reg, u16 clear, u16 set)
//---------------------------------------------------------------------------------
{
u16 val = sdmmc_read16(reg);
val &= ~clear;
val |= set;
sdmmc_write16(reg, val);
}
//---------------------------------------------------------------------------------
static inline void setckl(u32 data)
//---------------------------------------------------------------------------------
{
sdmmc_mask16(REG_SDCLKCTL, 0x100, 0);
sdmmc_mask16(REG_SDCLKCTL, 0x2FF, data & 0x2FF);
sdmmc_mask16(REG_SDCLKCTL, 0x0, 0x100);
}
#endif

145
arm9/Makefile Normal file
View File

@ -0,0 +1,145 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
endif
include $(DEVKITARM)/ds_rules
# If on a tagged commit, use just tag
ifneq ($(shell echo $(shell git tag -l --points-at HEAD) | head -c 1),)
GIT_VER := $(shell git tag -l --points-at HEAD)
else
GIT_VER := $(shell git describe --abbrev=0 --tags)-$(shell git rev-parse --short=7 HEAD)
endif
# Ensure version.h exists
ifeq (,$(wildcard include/version.h))
$(shell mkdir -p include)
$(shell touch include/version.h)
endif
# Print new version if changed
ifeq (,$(findstring $(GIT_VER), $(shell cat include/version.h)))
$(shell printf "#ifndef VERSION_H\n#define VERSION_H\n\n#define VERSION \"$(GIT_VER)\"\n\n#endif // VERSION_H\n" > include/version.h)
endif
#---------------------------------------------------------------------------------
# BUILD is the directory where object files & intermediate files will be placed
# SOURCES is a list of directories containing source code
# INCLUDES is a list of directories containing extra header files
# DATA is a list of directories containing binary files
# all directories are relative to this makefile
#---------------------------------------------------------------------------------
BUILD := build
SOURCES := src src/nand src/nand/polarssl src/nand/twltool
INCLUDES := include src/nand
DATA :=
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -mthumb -mthumb-interwork
CFLAGS := -g -Wall -O2\
-march=armv5te -mtune=arm946e-s -fomit-frame-pointer\
-ffast-math \
$(ARCH)
CFLAGS += $(INCLUDE) -DARM9
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
ASFLAGS := -g $(ARCH) -march=armv5te -mtune=arm946e-s
LDFLAGS = -specs=ds_arm9.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
#---------------------------------------------------------------------------------
# any extra libraries we wish to link with the project
#---------------------------------------------------------------------------------
LIBS := -lfat -lnds9
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS := $(LIBNDS)
#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#---------------------------------------------------------------------------------
ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------
export ARM9ELF := $(CURDIR)/$(TARGET).elf
export DEPSDIR := $(CURDIR)/$(BUILD)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
#---------------------------------------------------------------------------------
export LD := $(CC)
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
export LD := $(CXX)
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------
export OFILES := $(addsuffix .o,$(BINFILES)) \
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
-I$(CURDIR)/$(BUILD)
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
.PHONY: $(BUILD) clean
#---------------------------------------------------------------------------------
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) *.elf *.nds* *.bin
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
$(ARM9ELF) : $(OFILES)
@echo linking $(notdir $@)
@$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPSDIR)/*.d
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

275
arm9/src/backupmenu.c Normal file
View File

@ -0,0 +1,275 @@
#include "install.h"
#include "main.h"
#include "menu.h"
#include "rom.h"
#include "storage.h"
#include "message.h"
#include "nand/nandio.h"
#include <dirent.h>
#include <sys/stat.h>
enum {
BACKUP_MENU_RESTORE,
BACKUP_MENU_DELETE,
BACKUP_MENU_BACK
};
static void generateList(Menu* m);
static void printItem(Menu* m);
static int subMenu();
static bool delete(Menu* m);
void backupMenu()
{
clearScreen(&topScreen);
Menu* m = newMenu();
setMenuHeader(m, "BACKUP MENU");
generateList(m);
//no files found
if (m->itemCount <= 0)
{
messageBox("\x1B[33mNo backups found.\n\x1B[47m");
}
else
{
while (!programEnd)
{
swiWaitForVBlank();
scanKeys();
if (moveCursor(m))
{
if (m->changePage != 0)
generateList(m);
printMenu(m);
printItem(m);
}
if (keysDown() & KEY_B || m->itemCount <= 0)
break;
else if (keysDown() & KEY_A)
{
switch (subMenu())
{
case BACKUP_MENU_RESTORE:
install(m->items[m->cursor].value, false);
break;
case BACKUP_MENU_DELETE:
{
if (delete(m))
{
resetMenu(m);
generateList(m);
}
}
break;
}
printMenu(m);
}
}
}
freeMenu(m);
}
static int subMenu()
{
int result = -1;
Menu* m = newMenu();
addMenuItem(m, "Restore", NULL, 0);
addMenuItem(m, "Delete", NULL, 0);
addMenuItem(m, "Back - [B]", NULL, 0);
printMenu(m);
while (!programEnd)
{
swiWaitForVBlank();
scanKeys();
if (moveCursor(m))
printMenu(m);
if (keysDown() & KEY_B)
break;
else if (keysDown() & KEY_A)
{
result = m->cursor;
break;
}
}
freeMenu(m);
return result;
}
static void generateList(Menu* m)
{
if (!m) return;
//reset menu
clearMenu(m);
m->page += sign(m->changePage);
m->changePage = 0;
bool done = false;
struct dirent* ent;
DIR* dir = opendir(BACKUP_PATH);
if (dir)
{
int count = 0;
while ( (ent = readdir(dir)) && !done)
{
if (ent->d_name[0] == '.')
continue;
if (ent->d_type == DT_DIR)
{
if (count < m->page * ITEMS_PER_PAGE)
count += 1;
else
{
if (m->itemCount >= ITEMS_PER_PAGE)
done = true;
else
{
char* fpath = (char*)malloc(strlen(BACKUP_PATH) + strlen(ent->d_name) + 8);
sprintf(fpath, "%s/%s", BACKUP_PATH, ent->d_name);
addMenuItem(m, ent->d_name, fpath, 1);
}
}
}
else
{
if (strcasecmp(strrchr(ent->d_name, '.'), ".nds") == 0 ||
strcasecmp(strrchr(ent->d_name, '.'), ".app") == 0 ||
strcasecmp(strrchr(ent->d_name, '.'), ".dsi") == 0 ||
strcasecmp(strrchr(ent->d_name, '.'), ".ids") == 0 ||
strcasecmp(strrchr(ent->d_name, '.'), ".srl") == 0 ||
strcasecmp(strrchr(ent->d_name, '.'), ".cia") == 0)
{
if (count < m->page * ITEMS_PER_PAGE)
count += 1;
else
{
if (m->itemCount >= ITEMS_PER_PAGE)
done = true;
else
{
char* fpath = (char*)malloc(strlen(BACKUP_PATH) + strlen(ent->d_name) + 8);
sprintf(fpath, "%s/%s", BACKUP_PATH, ent->d_name);
addMenuItem(m, ent->d_name, fpath, 0);
free(fpath);
}
}
}
}
}
}
closedir(dir);
sortMenuItems(m);
m->nextPage = done;
if (m->cursor >= m->itemCount)
m->cursor = m->itemCount - 1;
printItem(m);
printMenu(m);
}
static void printItem(Menu* m)
{
if (!m) return;
if (m->itemCount <= 0) return;
if (m->items[m->cursor].directory)
clearScreen(&topScreen);
else
printRomInfo(m->items[m->cursor].value);
}
static bool delete(Menu* m)
{
if (!m) return false;
char* label = m->items[m->cursor].label;
char* fpath = m->items[m->cursor].value;
bool result = false;
bool choice = NO;
{
const char str[] = "Are you sure you want to delete\n";
char* msg = (char*)malloc(strlen(str) + strlen(label) + 2);
sprintf(msg, "%s%s?", str, label);
choice = choiceBox(msg);
free(msg);
}
if (choice == YES)
{
if (!fpath)
{
messageBox("\x1B[31mFailed to delete backup.\n\x1B[47m");
}
else
{
if (access(fpath, F_OK) != 0)
{
messageBox("\x1B[31mFailed to delete backup.\n\x1B[47m");
}
else
{
clearScreen(&bottomScreen);
//app
remove(fpath);
//tmd
strcpy(strrchr(fpath, '.'), ".tmd");
remove(fpath);
//public save
strcpy(strrchr(fpath, '.'), ".pub");
remove(fpath);
//private save
strcpy(strrchr(fpath, '.'), ".prv");
remove(fpath);
//banner save
strcpy(strrchr(fpath, '.'), ".bnr");
remove(fpath);
result = true;
messagePrint("\x1B[42m\nBackup deleted.\n\x1B[47m");
}
}
}
return result;
}

871
arm9/src/install.c Normal file
View File

@ -0,0 +1,871 @@
#include "install.h"
#include "sav.h"
#include "main.h"
#include "message.h"
#include "maketmd.h"
#include "nand/crypto.h"
#include "nand/nandio.h"
#include "nand/ticket0.h"
#include "rom.h"
#include "storage.h"
#include <dirent.h>
#include <errno.h>
#include <sys/stat.h>
static bool _titleIsUsed(tDSiHeader* h)
{
if (!h) return false;
char path[64];
sprintf(path, "%s:/title/%08x/%08x/", sdnandMode ? "sd" : "nand", (unsigned int)h->tid_high, (unsigned int)h->tid_low);
return dirExists(path);
}
//patch homebrew roms if gameCode is #### or null
static bool _patchGameCode(tDSiHeader* h)
{
if (!h) return false;
if ((strcmp(h->ndshdr.gameCode, "####") == 0 && h->tid_low == 0x23232323) || (!*h->ndshdr.gameCode && h->tid_low == 0))
{
iprintf("Fixing Game Code...");
swiWaitForVBlank();
//set as standard app
h->tid_high = 0x00030004;
do {
do {
//generate a random game code
for (int i = 0; i < 4; i++)
h->ndshdr.gameCode[i] = 'A' + (rand() % 26);
}
while (h->ndshdr.gameCode[0] == 'A'); //first letter shouldn't be A
//correct title id
h->tid_low = ( (h->ndshdr.gameCode[0] << 24) | (h->ndshdr.gameCode[1] << 16) | (h->ndshdr.gameCode[2] << 8) | h->ndshdr.gameCode[3] );
}
while (_titleIsUsed(h));
iprintf("\x1B[42m"); //green
iprintf("Done\n");
iprintf("\x1B[47m"); //white
return true;
}
return false;
}
static bool _iqueHack(tDSiHeader* h)
{
if (!h) return false;
if (h->ndshdr.reserved1[8] == 0x80)
{
iprintf("iQue Hack...");
h->ndshdr.reserved1[8] = 0x00;
iprintf("\x1B[42m"); //green
iprintf("Done\n");
iprintf("\x1B[47m"); //white
return true;
}
return false;
}
static unsigned long long _getSaveDataSize(tDSiHeader* h)
{
unsigned long long size = 0;
if (h)
{
size += h->public_sav_size;
size += h->private_sav_size;
//banner.sav
if (h->appflags & 0x4)
size += 0x4000;
}
return size;
}
static bool _checkSdSpace(unsigned long long size)
{
iprintf("Enough room on SD card?...");
swiWaitForVBlank();
if (getSDCardFree() < size)
{
iprintf("\x1B[31m"); //red
iprintf("No\n");
iprintf("\x1B[47m"); //white
return false;
}
iprintf("\x1B[42m"); //green
iprintf("Yes\n");
iprintf("\x1B[47m"); //white
return true;
}
static bool _checkDsiSpace(unsigned long long size, bool systemApp)
{
iprintf("Enough room on DSi?...");
swiWaitForVBlank();
//ensure there's at least 1 MiB free, to leave margin for error
if (((systemApp ? getDsiRealFree() : getDsiFree()) < size) || (((systemApp ? getDsiRealFree() : getDsiFree()) - size) < (1 << 20)))
{
iprintf("\x1B[31m"); //red
iprintf("No\n");
iprintf("\x1B[47m"); //white
return false;
}
iprintf("\x1B[42m"); //green
iprintf("Yes\n");
iprintf("\x1B[47m"); //white
return true;
}
static bool _openMenuSlot()
{
iprintf("Open DSi menu slot?...");
swiWaitForVBlank();
if (getMenuSlotsFree() <= 0)
{
iprintf("\x1B[31m"); //red
iprintf("No\n");
iprintf("\x1B[47m"); //white
return false;
}
iprintf("\x1B[42m"); //green
iprintf("Yes\n");
iprintf("\x1B[47m"); //white
return true;
}
static void _createPublicSav(tDSiHeader* h, char* dataPath)
{
if (!h) return;
if (h->public_sav_size > 0)
{
iprintf("Creating public.sav...");
swiWaitForVBlank();
if (!dataPath)
{
iprintf("\x1B[31m"); //red
iprintf("Failed\n");
iprintf("\x1B[47m"); //white
}
else
{
char* publicPath = (char*)malloc(strlen(dataPath) + strlen("/public.sav") + 1);
sprintf(publicPath, "%s/public.sav", dataPath);
FILE* f = fopen(publicPath, "wb");
if (!f)
{
iprintf("\x1B[31m"); //red
iprintf("Failed\n");
iprintf("\x1B[47m"); //white
}
else
{
fseek(f, h->public_sav_size-1, SEEK_SET);
fputc(0, f);
initFatHeader(f);
iprintf("\x1B[42m"); //green
iprintf("Done\n");
iprintf("\x1B[47m"); //white
}
fclose(f);
free(publicPath);
}
}
}
static void _createPrivateSav(tDSiHeader* h, char* dataPath)
{
if (!h) return;
if (h->private_sav_size > 0)
{
iprintf("Creating private.sav...");
swiWaitForVBlank();
if (!dataPath)
{
iprintf("\x1B[31m"); //red
iprintf("Failed\n");
iprintf("\x1B[47m"); //white
}
else
{
char* privatePath = (char*)malloc(strlen(dataPath) + strlen("/private.sav") + 1);
sprintf(privatePath, "%s/private.sav", dataPath);
FILE* f = fopen(privatePath, "wb");
if (!f)
{
iprintf("\x1B[31m"); //red
iprintf("Failed\n");
iprintf("\x1B[47m"); //white
}
else
{
fseek(f, h->private_sav_size-1, SEEK_SET);
fputc(0, f);
initFatHeader(f);
iprintf("\x1B[42m"); //green
iprintf("Done\n");
iprintf("\x1B[47m"); //white
}
fclose(f);
free(privatePath);
}
}
}
static void _createBannerSav(tDSiHeader* h, char* dataPath)
{
if (!h) return;
if (h->appflags & 0x4)
{
iprintf("Creating banner.sav...");
swiWaitForVBlank();
if (!dataPath)
{
iprintf("\x1B[31m"); //red
iprintf("Failed\n");
iprintf("\x1B[47m"); //white
}
else
{
char* bannerPath = (char*)malloc(strlen(dataPath) + strlen("/banner.sav") + 1);
sprintf(bannerPath, "%s/banner.sav", dataPath);
FILE* f = fopen(bannerPath, "wb");
if (!f)
{
iprintf("\x1B[31m"); //red
iprintf("Failed\n");
iprintf("\x1B[47m"); //white
}
else
{
fseek(f, 0x4000 - 1, SEEK_SET);
fputc(0, f);
iprintf("\x1B[42m"); //green
iprintf("Done\n");
iprintf("\x1B[47m"); //white
}
fclose(f);
free(bannerPath);
}
}
}
static void _createTicket(tDSiHeader *h, char* ticketPath)
{
if (!h) return;
iprintf("Forging ticket...");
swiWaitForVBlank();
if (!ticketPath)
{
iprintf("\x1B[31m"); //red
iprintf("Failed\n");
iprintf("\x1B[47m"); //white
}
else
{
const u32 encryptedSize = sizeof(ticket_v0_t) + 0x20;
u8 *buffer = (u8*)memalign(4, encryptedSize); //memalign might be needed for encryption, but not sure
memset(buffer, 0, encryptedSize);
ticket_v0_t *ticket = (ticket_v0_t*)buffer;
ticket->sig_type[0] = 0x00;
ticket->sig_type[1] = 0x01;
ticket->sig_type[2] = 0x00;
ticket->sig_type[3] = 0x01;
strcpy(ticket->issuer, "Root-CA00000001-XS00000006");
PUT_UINT32_BE(h->tid_high, ticket->title_id, 0);
PUT_UINT32_BE(h->tid_low, ticket->title_id, 4);
memset(ticket->content_access_permissions, 0xFF, 0x20);
// Encrypt
if (dsi_es_block_crypt(buffer, encryptedSize, ENCRYPT) != 0)
{
iprintf("\x1B[31m"); //red
iprintf("Failed\n");
iprintf("\x1B[47m"); //white
free(buffer);
return;
}
FILE *file = fopen(ticketPath, "wb");
if (!file)
{
iprintf("\x1B[31m"); //red
iprintf("Failed\n");
iprintf("\x1B[47m"); //white
free(buffer);
return;
}
if (fwrite(buffer, 1, encryptedSize, file) != encryptedSize)
{
iprintf("\x1B[31m"); //red
iprintf("Failed\n");
iprintf("\x1B[47m"); //white
}
else
{
iprintf("\x1B[42m"); //green
iprintf("Done\n");
iprintf("\x1B[47m"); //white
}
free(buffer);
fclose(file);
}
}
bool install(char* fpath, bool systemTitle)
{
bool result = false;
//check battery level
while (batteryLevel < 7 && !charging)
{
if (choiceBox("\x1B[47mBattery is too low!\nPlease plug in the console.\n\nContinue?") == NO)
return false;
}
//start installation
clearScreen(&bottomScreen);
tDSiHeader* h = getRomHeader(fpath);
if (!h)
{
iprintf("\x1B[31m"); //red
iprintf("Error: ");
iprintf("\x1B[33m"); //yellow
iprintf("Could not open file.\n");
iprintf("\x1B[47m"); //white
goto error;
}
else
{
bool fixHeader = false;
if (_patchGameCode(h))
fixHeader = true;
//title id must be one of these
if (h->tid_high == 0x00030004 || // DSiWare
h->tid_high == 0x00030005 || // "unimportant" system titles
h->tid_high == 0x00030011 || // SRLs in the TWL SDK
h->tid_high == 0x00030015 || // system titles
h->tid_high == 0x00030017) // Launcher
{}
else
{
iprintf("\x1B[31m"); //red
iprintf("Error: ");
iprintf("\x1B[33m"); //yellow
iprintf("This is not a DSi rom.\n");
iprintf("\x1B[47m"); //white
goto error;
}
//patch dev titles to system titles on SysNAND.
//
//software released through the TWL SDK usually comes as a TAD and an SRL
//things like NandFiler have a TAD with a TID of 0x00030015 and an SRL with 0x00030011
//the TAD is the installable version, so I'm assuming that 0x00030015 is what the console wants to see on NAND
//this changes the SRL TID accordingly
//not entirely sure why there's even any difference. I think the installed TAD and SRL the same as each other (minus the TID)
if(!sdnandMode && h->tid_high == 0x00030011)
{
h->tid_high = 0x00030015;
fixHeader = true;
}
//offer to patch system titles to normal DSiWare on SysNAND
if(!sdnandMode && h->tid_high != 0x00030004 && h->tid_high != 0x00030017) //do not allow patching home menus to be normal DSiWare! This will trigger "ERROR! - 0x0000000000000008 HWINFO_SECURE" on prototype launchers. May also cause issues on the prod versions.
{
if(choiceBox("This is set as a system/dev\ntitle, would you like to patch\nit to be a normal DSiWare?\n\nThis is safer, but invalidates\nRSA checks and may not work.\n\nIf the title is homebrew this isstrongly recommended.") == YES)
{
h->tid_high = 0x00030004;
fixHeader = true;
}
}
//offer to patch home menus to be system titles on SysNAND
if(!sdnandMode && h->tid_high == 0x00030017)
{
if(choiceBox("This title is a home menu.\nWould you like to patch it to bea system title?\n\nThis is safer and prevents your\nhome menu from being hidden.") == YES)
{
h->tid_high = 0x00030015;
fixHeader = true;
}
}
//no system titles without Unlaunch
if (!unlaunchFound && h->tid_high != 0x00030004)
{
iprintf("\x1B[31m"); //red
iprintf("Error: ");
iprintf("\x1B[33m"); //yellow
iprintf("This title cannot be\ninstalled without Unlaunch.\n");
iprintf("\x1B[47m"); //white
goto error;
}
//blacklisted titles
{
//tid without region
u32 tidLow = (h->tid_low & 0xFFFFFF00);
if (!sdnandMode && (
(h->tid_high == 0x00030005 && (
tidLow == 0x484e4400 || // DS Download Play
tidLow == 0x484e4500 || // PictoChat
tidLow == 0x484e4900 || // Nintendo DSi Camera
tidLow == 0x484e4a00 || // Nintendo Zone
tidLow == 0x484e4b00 // Nintendo DSi Sound
)) || (h->tid_high == 0x00030011 && (
tidLow == 0x30535500 || // Twl SystemUpdater
tidLow == 0x34544e00 || // TwlNmenu
tidLow == 0x54574c00 // TWL EVA
)) || (h->tid_high == 0x00030015 && (
tidLow == 0x484e4200 || // System Settings
tidLow == 0x484e4600 || // Nintendo DSi Shop
tidLow == 0x34544e00 // TwlNmenu
)) || (h->tid_high == 0x00030017 && (
tidLow == 0x484e4100 // Launcher
))) && (
(h->tid_low & 0xFF) == region || // Only blacklist console region, or the following programs that have all-region codes:
h->tid_low == 0x484e4541 || // PictoChat
h->tid_low == 0x484e4441 || // Download Play
h->tid_low == 0x30535541 || // Twl SystemUpdater (iirc one version fits in NAND)
h->tid_low == 0x34544e41 || // TwlNmenu (blocking due to potential to uninstall system titles)
h->tid_low == 0x54574c41 || // TWL EVA
region == 0 //if the region check failed somehow, blacklist everything
))
{
//check if title exists, if it does then show any error
//otherwise allow reinstalling it
char path[PATH_MAX];
sprintf(path, "nand:/title/%08lx/%08lx/content/title.tmd", h->tid_high, h->tid_low);
if (access(path, F_OK) == 0)
{
iprintf("\x1B[31m"); //red
iprintf("Error: ");
iprintf("\x1B[33m"); //yellow
iprintf("This title cannot be\ninstalled to SysNAND.\n");
iprintf("\x1B[47m"); //white
goto error;
}
}
}
//confirmation message
{
const char system[] = "\x1B[41mWARNING:\x1B[47m This is a system app,\ninstalling it is potentially\nmore risky than regular DSiWare.\n\x1B[33m";
const char areYouSure[] = "Are you sure you want to install\n";
char* msg = (char*)malloc(strlen(system) + strlen(areYouSure) + strlen(fpath) + 2);
if (sdnandMode || h->tid_high == 0x00030004)
sprintf(msg, "%s%s?\n", areYouSure, fpath);
else
sprintf(msg, "%s%s%s?\n", system, areYouSure, fpath);
bool choice = choiceBox(msg);
free(msg);
if (choice == NO)
return false;
}
if (!sdnandMode && !nandio_unlock_writing())
return false;
clearScreen(&bottomScreen);
iprintf("Installing %s\n\n", fpath); swiWaitForVBlank();
//check for legit TMD, if found we'll generate a ticket which increases the size
int extensionPos = strrchr(fpath, '.') - fpath;
char tmdPath[PATH_MAX];
strcpy(tmdPath, fpath);
strcpy(tmdPath + extensionPos, ".tmd");
//DSi TMDs are 520, TMDs from NUS are 2,312. If 2,312 we can simply trim it to 520
int tmdSize = getFileSizePath(tmdPath);
bool tmdFound = (tmdSize == 520) || (tmdSize == 2312);
if (access(tmdPath, F_OK) == 0 && !tmdFound)
{
if (choicePrint("Incorrect TMD.\nInstall anyway?") == YES)
tmdFound = false;
else
goto error;
}
else if(!sdnandMode && !unlaunchPatches && access(tmdPath, F_OK) != 0)
{
if (choicePrint("TMD not found, game cannot be\nplayed without Unlaunch's\nlauncher patches.\nSee wiki for how to get a TMD.\n\nInstall anyway?") == YES)
tmdFound = false;
else
goto error;
}
//get install size
iprintf("Install Size: ");
swiWaitForVBlank();
u32 clusterSize = getDsiClusterSize();
unsigned long long fileSize = getRomSize(fpath), fileSizeOnDisk = fileSize;
if ((fileSizeOnDisk % clusterSize) != 0)
fileSizeOnDisk += clusterSize - (fileSizeOnDisk % clusterSize);
//file + saves + TMD (rounded up to cluster size)
unsigned long long installSize = fileSizeOnDisk + _getSaveDataSize(h) + clusterSize;
if (tmdFound) installSize += clusterSize; //ticket, rounded up to cluster size
printBytes(installSize);
iprintf("\n");
if (sdnandMode && !_checkSdSpace(installSize))
goto error;
//system title patch
if (systemTitle)
{
iprintf("System Title Patch...");
swiWaitForVBlank();
h->tid_high = 0x00030015;
iprintf("\x1B[42m"); //green
iprintf("Done\n");
iprintf("\x1B[47m"); //white
fixHeader = true;
}
//check that there's space on nand
if (!_checkDsiSpace(installSize, (h->tid_high != 0x00030004)))
{
if (sdnandMode && choicePrint("Install as system title?"))
{
h->tid_high = 0x00030015;
fixHeader = true;
}
else
{
goto error;
}
}
//check for saves
char pubPath[PATH_MAX];
strcpy(pubPath, fpath);
strcpy(pubPath + extensionPos, ".pub");
bool pubFound = getFileSizePath(pubPath) == h->public_sav_size;
if (access(pubPath, F_OK) == 0 && !pubFound)
{
if (choicePrint("Incorrect public save.\nInstall anyway?") == YES)
pubFound = false;
else
goto error;
}
char prvPath[PATH_MAX];
strcpy(prvPath, fpath);
strcpy(prvPath + extensionPos, ".prv");
bool prvFound = getFileSizePath(prvPath) == h->private_sav_size;
if (access(prvPath, F_OK) == 0 && !prvFound)
{
if (choicePrint("Incorrect private save.\nInstall anyway?") == YES)
prvFound = false;
else
goto error;
}
char bnrPath[PATH_MAX];
strcpy(bnrPath, fpath);
strcpy(bnrPath + extensionPos, ".bnr");
bool bnrFound = getFileSizePath(bnrPath) == 0x4000;
if (access(bnrPath, F_OK) == 0 && !bnrFound)
{
if (choicePrint("Incorrect banner save.\nInstall anyway?") == YES)
bnrFound = false;
else
goto error;
}
if (_iqueHack(h))
fixHeader = true;
if (fixHeader && tmdFound)
{
if (choicePrint("Legit TMD cannot be used.\nInstall anyway?") == YES)
tmdFound = false;
else
goto error;
}
//create title directory /title/XXXXXXXX/XXXXXXXX
char dirPath[32];
mkdir(sdnandMode ? "sd:/title" : "nand:/title", 0777);
sprintf(dirPath, "%s:/title/%08x", sdnandMode ? "sd" : "nand", (unsigned int)h->tid_high);
mkdir(dirPath, 0777);
sprintf(dirPath, "%s:/title/%08x/%08x", sdnandMode ? "sd" : "nand", (unsigned int)h->tid_high, (unsigned int)h->tid_low);
//check if title is free
if (_titleIsUsed(h))
{
char msg[64];
sprintf(msg, "Title %08x is already used.\nInstall anyway?", (unsigned int)h->tid_low);
if (choicePrint(msg) == NO)
goto error;
else
{
iprintf("\nDeleting:\n");
deleteDir(dirPath);
iprintf("\n");
}
}
if (!_openMenuSlot())
goto error;
mkdir(dirPath, 0777);
//content folder /title/XXXXXXXX/XXXXXXXXX/content
{
char contentPath[64];
sprintf(contentPath, "%s/content", dirPath);
mkdir(contentPath, 0777);
u8 appVersion = 0;
if (tmdFound)
{
FILE *file = fopen(tmdPath, "rb");
if (file)
{
fseek(file, 0x1E7, SEEK_SET);
fread(&appVersion, sizeof(appVersion), 1, file);
fclose(file);
}
}
//create 000000##.app
{
iprintf("Creating 000000%02x.app...", appVersion);
swiWaitForVBlank();
char appPath[80];
sprintf(appPath, "%s/000000%02x.app", contentPath, appVersion);
//copy nds file to app
{
int result = 0;
if (!romIsCia(fpath))
result = copyFile(fpath, appPath);
else
result = copyFilePart(fpath, 0x3900, fileSize, appPath);
if (result != 0)
{
iprintf("\x1B[31m"); //red
iprintf("Failed\n");
iprintf("\x1B[33m"); //yellow
iprintf("%s\n", appPath);
iprintf("%s\n", strerror(errno));
iprintf("\x1B[47m"); //white
goto error;
}
iprintf("\x1B[42m"); //green
iprintf("Done\n");
iprintf("\x1B[47m"); //white
}
//pad out banner if it is the last part of the file
{
if (h->ndshdr.bannerOffset > (fileSize - 0x23C0))
{
iprintf("Padding banner...");
swiWaitForVBlank();
if (padFile(appPath, h->ndshdr.bannerOffset + 0x23C0 - fileSize) == false)
{
iprintf("\x1B[31m"); //red
iprintf("Failed\n");
iprintf("\x1B[47m"); //white
}
else
{
iprintf("\x1B[42m"); //green
iprintf("Done\n");
iprintf("\x1B[47m"); //white
}
}
}
//update header
{
if (fixHeader)
{
iprintf("Fixing header...");
swiWaitForVBlank();
//fix header checksum
h->ndshdr.headerCRC16 = swiCRC16(0xFFFF, h, 0x15E);
//fix RSA signature
u8 buffer[20];
swiSHA1Calc(&buffer, h, 0xE00);
memcpy(&(h->rsa_signature[0x6C]), buffer, 20);
FILE* f = fopen(appPath, "r+");
if (!f)
{
iprintf("\x1B[31m"); //red
iprintf("Failed\n");
iprintf("\x1B[47m"); //white
}
else
{
fseek(f, 0, SEEK_SET);
fwrite(h, sizeof(tDSiHeader), 1, f);
iprintf("\x1B[42m"); //green
iprintf("Done\n");
iprintf("\x1B[47m"); //white
}
fclose(f);
}
}
//make/copy TMD
char newTmdPath[80];
sprintf(newTmdPath, "%s/title.tmd", contentPath);
if (tmdFound)
{
if (copyFilePart(tmdPath, 0, 520, newTmdPath) != 0)
goto error;
}
else
{
if (maketmd(appPath, newTmdPath) != 0)
goto error;
}
}
}
//data folder
{
char dataPath[64];
sprintf(dataPath, "%s/data", dirPath);
mkdir(dataPath, 0777);
if (pubFound)
{
char newPubPath[80];
sprintf(newPubPath, "%s/public.sav", dataPath);
copyFile(pubPath, newPubPath);
}
else
{
_createPublicSav(h, dataPath);
}
if (prvFound)
{
char newPrvPath[80];
sprintf(newPrvPath, "%s/private.sav", dataPath);
copyFile(prvPath, newPrvPath);
}
else
{
_createPrivateSav(h, dataPath);
}
if (bnrFound)
{
char newBnrPath[80];
sprintf(newBnrPath, "%s/banner.sav", dataPath);
copyFile(bnrPath, newBnrPath);
}
else
{
_createBannerSav(h, dataPath);
}
}
//ticket folder /ticket/XXXXXXXX
if (tmdFound)
{
//ensure folders exist
char ticketPath[32];
siprintf(ticketPath, "%s:/ticket", sdnandMode ? "sd" : "nand");
mkdir(ticketPath, 0777);
siprintf(ticketPath, "%s/%08lx", ticketPath, h->tid_high);
mkdir(ticketPath, 0777);
//actual tik path
siprintf(ticketPath, "%s/%08lx.tik", ticketPath, h->tid_low);
if (access(ticketPath, F_OK) != 0 || (choicePrint("Ticket already exists.\nKeep it? (recommended)") == NO && choicePrint("Are you sure?") == YES))
_createTicket(h, ticketPath);
}
//end
result = true;
iprintf("\x1B[42m"); //green
iprintf("\nInstallation complete.\n");
iprintf("\x1B[47m"); //white
iprintf("Back - [B]\n");
keyWait(KEY_A | KEY_B);
goto complete;
}
error:
messagePrint("\x1B[31m\nInstallation failed.\n\x1B[47m");
complete:
free(h);
if (!sdnandMode)
nandio_lock_writing();
return result;
}

8
arm9/src/install.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef INSTALL_H
#define INSTALL_H
#include <nds/ndstypes.h>
bool install(char* fpath, bool systemTitle);
#endif

320
arm9/src/installmenu.c Normal file
View File

@ -0,0 +1,320 @@
#include "main.h"
#include "rom.h"
#include "install.h"
#include "menu.h"
#include "storage.h"
#include "message.h"
#include <dirent.h>
enum {
INSTALL_MENU_INSTALL,
INSTALL_MENU_SYSTEM_TITLE,
INSTALL_MENU_DELETE,
INSTALL_MENU_BACK
};
static char currentDir[512] = "";
static void generateList(Menu* m);
static void printItem(Menu* m);
static int subMenu();
static bool delete(Menu* m);
static void _setHeader(Menu* m)
{
if (!m) return;
if (currentDir[0] == '\0')
setMenuHeader(m, "sd:/");
else
setMenuHeader(m, currentDir);
}
void installMenu()
{
Menu* m = newMenu();
_setHeader(m);
generateList(m);
//no files found
/* if (m->itemCount <= 0)
{
clearScreen(&bottomScreen);
iprintf("\x1B[31m"); //red
iprintf("No files found.\n");
iprintf("\x1B[47m"); //white
iprintf("\nBack - [B]\n");
keyWait(KEY_B | KEY_A | KEY_START);
}
else*/
{
while (!programEnd)
{
swiWaitForVBlank();
scanKeys();
if (moveCursor(m))
{
if (m->changePage != 0)
generateList(m);
printMenu(m);
printItem(m);
}
//back
if (keysDown() & KEY_B)
{
char* ptr = strrchr(currentDir, '/');
if (ptr)
{
*ptr = '\0';
_setHeader(m);
resetMenu(m);
generateList(m);
printMenu(m);
}
else
{
break;
}
}
else if (keysDown() & KEY_X)
break;
//selection
else if (keysDown() & KEY_A)
{
if (m->itemCount > 0)
{
if (m->items[m->cursor].directory == false)
{
//nds file
switch (subMenu())
{
case INSTALL_MENU_INSTALL:
install(m->items[m->cursor].value, false);
break;
case INSTALL_MENU_SYSTEM_TITLE:
if (sdnandMode)
install(m->items[m->cursor].value, true);
break;
case INSTALL_MENU_DELETE:
{
if (delete(m))
{
resetMenu(m);
generateList(m);
}
}
break;
case INSTALL_MENU_BACK:
break;
}
}
else
{
//directory
sprintf(currentDir, "%s", m->items[m->cursor].value);
_setHeader(m);
resetMenu(m);
generateList(m);
}
printMenu(m);
}
}
}
}
freeMenu(m);
}
static void generateList(Menu* m)
{
if (!m) return;
//reset menu
clearMenu(m);
m->page += sign(m->changePage);
m->changePage = 0;
bool done = false;
struct dirent* ent;
DIR* dir = NULL;
if (currentDir[0] == '\0')
dir = opendir("sd:/");
else
dir = opendir(currentDir);
if (dir)
{
int count = 0;
while ( (ent = readdir(dir)) && !done)
{
if (ent->d_name[0] == '.')
continue;
if (ent->d_type == DT_DIR)
{
if (count < m->page * ITEMS_PER_PAGE)
count += 1;
else
{
if (m->itemCount >= ITEMS_PER_PAGE)
done = true;
else
{
char* fpath = (char*)malloc(strlen(currentDir) + strlen(ent->d_name) + 8);
sprintf(fpath, "%s/%s", currentDir, ent->d_name);
addMenuItem(m, ent->d_name, fpath, 1);
}
}
}
else
{
if (strcasecmp(strrchr(ent->d_name, '.'), ".nds") == 0 ||
strcasecmp(strrchr(ent->d_name, '.'), ".app") == 0 ||
strcasecmp(strrchr(ent->d_name, '.'), ".dsi") == 0 ||
strcasecmp(strrchr(ent->d_name, '.'), ".ids") == 0 ||
strcasecmp(strrchr(ent->d_name, '.'), ".srl") == 0 ||
strcasecmp(strrchr(ent->d_name, '.'), ".cia") == 0)
{
if (count < m->page * ITEMS_PER_PAGE)
count += 1;
else
{
if (m->itemCount >= ITEMS_PER_PAGE)
done = true;
else
{
char* fpath = (char*)malloc(strlen(currentDir) + strlen(ent->d_name) + 8);
sprintf(fpath, "%s/%s", currentDir, ent->d_name);
addMenuItem(m, ent->d_name, fpath, 0);
free(fpath);
}
}
}
}
}
}
closedir(dir);
sortMenuItems(m);
m->nextPage = done;
if (m->cursor >= m->itemCount)
m->cursor = m->itemCount - 1;
printItem(m);
printMenu(m);
}
static void printItem(Menu* m)
{
if (!m) return;
if (m->itemCount <= 0) return;
if (m->items[m->cursor].directory)
clearScreen(&topScreen);
else
printRomInfo(m->items[m->cursor].value);
}
static int subMenu()
{
int result = -1;
Menu* m = newMenu();
addMenuItem(m, "Install", NULL, 0);
addMenuItem(m, sdnandMode ? "Install as System Title" : "\x1B[37m[Disabled]\x1B[47m", NULL, 0);
addMenuItem(m, "Delete", NULL, 0);
addMenuItem(m, "Back - [B]", NULL, 0);
printMenu(m);
while (!programEnd)
{
swiWaitForVBlank();
scanKeys();
if (moveCursor(m))
printMenu(m);
if (keysDown() & KEY_B)
{
result = -1;
break;
}
else if (keysDown() & KEY_A)
{
result = m->cursor;
break;
}
}
freeMenu(m);
return result;
}
static bool delete(Menu* m)
{
if (!m) return false;
char* fpath = m->items[m->cursor].value;
bool result = false;
bool choice = NO;
{
char str[] = "Are you sure you want to delete\n";
char* msg = (char*)malloc(strlen(str) + strlen(fpath) + 1);
sprintf(msg, "%s%s", str, fpath);
choice = choiceBox(msg);
free(msg);
}
if (choice == YES)
{
if (!fpath)
{
messageBox("\x1B[31mCould not delete file.\x1B[47m");
}
else
{
if (remove(fpath) == 0)
{
result = true;
messageBox("\x1B[42mFile deleted.\x1B[47m");
}
else
{
messageBox("\x1B[31mCould not delete file.\x1B[47m");
}
}
}
return result;
}

313
arm9/src/main.c Normal file
View File

@ -0,0 +1,313 @@
#include "main.h"
#include "menu.h"
#include "message.h"
#include "nand/nandio.h"
#include "storage.h"
#include "version.h"
#include <dirent.h>
#include <time.h>
bool programEnd = false;
bool sdnandMode = true;
bool unlaunchFound = false;
bool unlaunchPatches = false;
bool devkpFound = false;
bool launcherDSiFound = false;
bool arm7Exiting = false;
bool charging = false;
u8 batteryLevel = 0;
u8 region = 0;
PrintConsole topScreen;
PrintConsole bottomScreen;
enum {
MAIN_MENU_MODE,
MAIN_MENU_INSTALL,
MAIN_MENU_TITLES,
MAIN_MENU_BACKUP,
MAIN_MENU_TEST,
MAIN_MENU_FIX,
MAIN_MENU_DATA_MANAGEMENT,
MAIN_MENU_LANGUAGE_PATCHER,
MAIN_MENU_EXIT
};
static void _setupScreens()
{
REG_DISPCNT = MODE_FB0;
VRAM_A_CR = VRAM_ENABLE;
videoSetMode(MODE_0_2D);
videoSetModeSub(MODE_0_2D);
vramSetBankA(VRAM_A_MAIN_BG);
vramSetBankC(VRAM_C_SUB_BG);
consoleInit(&topScreen, 3, BgType_Text4bpp, BgSize_T_256x256, 31, 0, true, true);
consoleInit(&bottomScreen, 3, BgType_Text4bpp, BgSize_T_256x256, 31, 0, false, true);
clearScreen(&bottomScreen);
VRAM_A[100] = 0xFFFF;
}
static int _mainMenu(int cursor)
{
//top screen
clearScreen(&topScreen);
iprintf("\t\tNAND Title Manager\n");
iprintf("\t\t\tmodified from\n");
iprintf("\tTitle Manager for HiyaCFW\n");
iprintf("\nversion %s\n", VERSION);
iprintf("\n\n\x1B[41mWARNING:\x1B[47m This tool can write to\nyour internal NAND!\n\nThis always has a risk, albeit\nlow, of \x1B[41mbricking\x1B[47m your system\nand should be done with caution!\n");
iprintf("\n\t \x1B[46mhttps://dsi.cfw.guide\x1B[47m\n");
iprintf("\n\n \x1B[46mgithub.com/Epicpkmn11/NTM/wiki\x1B[47m\n");
iprintf("\x1b[22;0HJeff - 2018-2019");
iprintf("\x1b[23;0HPk11 - 2022-2023");
//menu
Menu* m = newMenu();
setMenuHeader(m, "MAIN MENU");
char modeStr[32], datamanStr[32], launcherStr[32];
sprintf(modeStr, "Mode: %s", sdnandMode ? "SDNAND" : "\x1B[41mSysNAND\x1B[47m");
sprintf(datamanStr, "\x1B[%02omEnable Data Management", devkpFound ? 037 : 047);
sprintf(launcherStr, "\x1B[%02omUninstall region mod", launcherDSiFound ? 047 : 037);
addMenuItem(m, modeStr, NULL, 0);
addMenuItem(m, "Install", NULL, 0);
addMenuItem(m, "Titles", NULL, 0);
addMenuItem(m, "Restore", NULL, 0);
addMenuItem(m, "Test", NULL, 0);
addMenuItem(m, "Fix FAT copy mismatch", NULL, 0);
addMenuItem(m, datamanStr, NULL, 0);
addMenuItem(m, launcherStr, NULL, 0);
addMenuItem(m, "\x1B[47mExit", NULL, 0);
m->cursor = cursor;
//bottom screen
printMenu(m);
while (!programEnd)
{
swiWaitForVBlank();
scanKeys();
if (moveCursor(m))
printMenu(m);
if (keysDown() & KEY_A)
break;
}
int result = m->cursor;
freeMenu(m);
return result;
}
void fifoHandlerPower(u32 value32, void* userdata)
{
if (value32 == 0x54495845) // 'EXIT'
{
programEnd = true;
arm7Exiting = true;
}
}
void fifoHandlerBattery(u32 value32, void* userdata)
{
batteryLevel = value32 & 0xF;
charging = (value32 & BIT(7)) != 0;
}
int main(int argc, char **argv)
{
srand(time(0));
keysSetRepeat(25, 5);
_setupScreens();
fifoSetValue32Handler(FIFO_USER_01, fifoHandlerPower, NULL);
fifoSetValue32Handler(FIFO_USER_03, fifoHandlerBattery, NULL);
//DSi check
if (!isDSiMode())
{
messageBox("\x1B[31mError:\x1B[33m This app is only for DSi.");
return 0;
}
//setup sd card access
if (!fatInitDefault())
{
messageBox("fatInitDefault()...\x1B[31mFailed\n\x1B[47m");
return 0;
}
//setup nand access
if (!fatMountSimple("nand", &io_dsi_nand))
{
messageBox("nand init \x1B[31mfailed\n\x1B[47m");
return 0;
}
//check for unlaunch and region
{
FILE *file = fopen("nand:/sys/HWINFO_S.dat", "rb");
if (file)
{
fseek(file, 0xA0, SEEK_SET);
u32 launcherTid;
fread(&launcherTid, sizeof(u32), 1, file);
fclose(file);
region = launcherTid & 0xFF;
char path[64];
sprintf(path, "nand:/title/00030017/%08lx/content/title.tmd", launcherTid);
unsigned long long tmdSize = getFileSizePath(path);
if (tmdSize > 520)
unlaunchFound = true;
//check if launcher patches are enabled
const static u32 tidValues[][2] = {
// {location, value}
{0xE439, 0x382E3176}, // 1.8
{0xB07C, 0x17484E41}, // 1.9
{0xB099, 0x17484E41}, // 2.0 (Normal)
{0xB079, 0x484E1841}, // 2.0 (Patched)
};
FILE *tmd = fopen(path, "rb");
if (tmd)
{
for (int i = 0; i < sizeof(tidValues) / sizeof(tidValues[0]); i++)
{
if (fseek(file, tidValues[i][0], SEEK_SET) == 0)
{
u32 tidVal;
fread(&tidVal, sizeof(u32), 1, file);
if (tidVal == tidValues[i][1])
{
unlaunchPatches = true;
break;
}
}
}
}
}
if (!unlaunchFound)
{
messageBox("Unlaunch not found. TMD files\nwill be required and there\nis a greater risk something\ncould go wrong.\n\nSee \x1B[46mhttps://dsi.cfw.guide/\x1B[47m to\ninstall.");
}
else if (!unlaunchPatches)
{
messageBox("Unlaunch's Launcher Patches are\nnot enabled. You will need to\nprovide TMD files or reinstall.\n\n\x1B[46mhttps://dsi.cfw.guide/\x1B[47m");
}
}
//check for dev.kp (Data Management visible)
devkpFound = (access("sd:/sys/dev.kp", F_OK) == 0);
//check for launcher.dsi (Language patcher)
launcherDSiFound = (access("nand:/launcher.dsi", F_OK) == 0);
messageBox("\x1B[41mWARNING:\x1B[47m This tool can write to\nyour internal NAND!\n\nThis always has a risk, albeit\nlow, of \x1B[41mbricking\x1B[47m your system\nand should be done with caution!\n\nIf you have not yet done so,\nyou should make a NAND backup.");
messageBox("If you are following a video\nguide, please stop.\n\nVideo guides for console moddingare often outdated or straight\nup incorrect to begin with.\n\nThe recommended guide for\nmodding your DSi is:\n\n\x1B[46mhttps://dsi.cfw.guide/\x1B[47m\n\nFor more information on using\nNTM, see the official wiki:\n\n\x1B[46mhttps://github.com/Epicpkmn11/\n\t\t\t\t\t\t\t\tNTM/wiki\x1B[47m");
//main menu
int cursor = 0;
while (!programEnd)
{
cursor = _mainMenu(cursor);
switch (cursor)
{
case MAIN_MENU_MODE:
sdnandMode = !sdnandMode;
devkpFound = (access(sdnandMode ? "sd:/sys/dev.kp" : "nand:/sys/dev.kp", F_OK) == 0);
break;
case MAIN_MENU_INSTALL:
installMenu();
break;
case MAIN_MENU_TITLES:
titleMenu();
break;
case MAIN_MENU_BACKUP:
backupMenu();
break;
case MAIN_MENU_TEST:
testMenu();
break;
case MAIN_MENU_FIX:
if (nandio_unlock_writing())
{
nandio_force_fat_fix();
nandio_lock_writing();
messageBox("Mismatch in FAT copies will be\nfixed on close.\n");
}
break;
case MAIN_MENU_DATA_MANAGEMENT:
if (!devkpFound && (choiceBox("Make Data Management visible\nin System Settings?") == YES) && (sdnandMode || nandio_unlock_writing()))
{
//ensure sys folder exists
if(access(sdnandMode ? "sd:/sys" : "nand:/sys", F_OK) != 0)
mkdir(sdnandMode ? "sd:/sys" : "nand:/sys", 0777);
//create empty file
FILE *file = fopen(sdnandMode ? "sd:/sys/dev.kp" : "nand:/sys/dev.kp", "wb");
fclose(file);
if(!sdnandMode)
nandio_lock_writing();
devkpFound = (access(sdnandMode ? "sd:/sys/dev.kp" : "nand:/sys/dev.kp", F_OK) == 0);
messageBox("Data Management is now visible\nin System Settings.\n");
}
break;
case MAIN_MENU_LANGUAGE_PATCHER:
if (launcherDSiFound && (choiceBox("Uninstall the language patched\nDSi Menu? (launcher.dsi)") == YES) && nandio_unlock_writing())
{
//delete launcher.dsi
remove("nand:/launcher.dsi");
nandio_lock_writing();
launcherDSiFound = (access("nand:/launcher.dsi", F_OK) == 0);
messageBox("The language patched DSi Menu\nhas been removed.\n");
}
break;
case MAIN_MENU_EXIT:
programEnd = true;
break;
}
}
clearScreen(&bottomScreen);
printf("Unmounting NAND...\n");
fatUnmount("nand:");
printf("Merging stages...\n");
nandio_shutdown();
fifoSendValue32(FIFO_USER_02, 0x54495845); // 'EXIT'
while (arm7Exiting)
swiWaitForVBlank();
return 0;
}
void clearScreen(PrintConsole* screen)
{
consoleSelect(screen);
consoleClear();
}

30
arm9/src/main.h Normal file
View File

@ -0,0 +1,30 @@
#ifndef MAIN_H
#define MAIN_H
#include <nds.h>
#include <fat.h>
#include <stdio.h>
extern bool programEnd;
extern bool sdnandMode;
extern bool unlaunchFound;
extern bool unlaunchPatches;
extern bool charging;
extern u8 batteryLevel;
extern u8 region;
void installMenu();
void titleMenu();
void backupMenu();
void testMenu();
extern PrintConsole topScreen;
extern PrintConsole bottomScreen;
void clearScreen(PrintConsole* screen);
#define abs(X) ( (X) < 0 ? -(X): (X) )
#define sign(X) ( ((X) > 0) - ((X) < 0) )
#define repeat(X) for (int _I_ = 0; _I_ < (X); _I_++)
#endif

191
arm9/src/maketmd.c Normal file
View File

@ -0,0 +1,191 @@
/*---------------------------------------------------------------------------------
maketmd.cpp -- TMD Creator for DSiWare Homebrew
Copyright (C) 2018
Przemyslaw Skryjomski (Tuxality)
Big thanks to:
Apache Thunder
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you
must not claim that you wrote the original software. If you use
this software in a product, an acknowledgment in the product
documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
---------------------------------------------------------------------------------*/
/* September 2018 - Jeff - Translated from C++ to C and uses libnds instead of openssl
Original: github.com/Tuxality/maketmd
*/
#include "maketmd.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <nds/sha1.h>
#include <nds/ndstypes.h>
#include <machine/endian.h>
//#define TMD_CREATOR_VER "0.2"
#define TMD_SIZE 0x208
#define SHA_BUFFER_SIZE 0x200
#define SHA_DIGEST_LENGTH 0x14
void tmd_create(uint8_t* tmd, FILE* app)
{
// Phase 1 - offset 0x18C (Title ID, first part)
{
fseek(app, 0x234, SEEK_SET);
uint32_t value;
fread(&value, 4, 1, app);
value = __bswap32(value);
memcpy(tmd + 0x18c, &value, 4);
}
// Phase 2 - offset 0x190 (Title ID, second part)
{
// We can take this also from 0x230, but reversed
fseek(app, 0x0C, SEEK_SET);
fread((char*)&tmd[0x190], 4, 1, app);
}
// Phase 3 - offset 0x198 (Group ID = '01')
{
fseek(app, 0x10, SEEK_SET);
fread((char*)&tmd[0x198], 2, 1, app);
}
// Phase 4 - offset 0x1AA (fill-in 0x80 value, 0x10 times)
{
for (size_t i = 0; i<0x10; i++)
{
tmd[0x1AA + i] = 0x80;
}
}
// Phase 5 - offset 0x1DE (number of contents = 1)
{
tmd[0x1DE] = 0x00;
tmd[0x1DF] = 0x01;
}
// Phase 6 - offset 0x1EA (type of content = 1)
{
tmd[0x1EA] = 0x00;
tmd[0x1EB] = 0x01;
}
// Phase 7 - offset, 0x1EC (file size, 8B)
uint32_t filesize = 0;
uint32_t fileread = 0;
{
fseek(app, 0, SEEK_END);
filesize = ftell(app);
uint32_t size = __bswap32(filesize);
// We only use 4B for size as for now
memcpy((tmd + 0x1F0), &size, sizeof(u32));
}
// Phase 8 - offset, 0x1F4 (SHA1 sum, 20B)
{
// Makes use of libnds
fseek(app, 0, SEEK_SET);
uint8_t buffer[SHA_BUFFER_SIZE] = { 0 };
uint32_t buffer_read = 0;
swiSHA1context_t ctx;
swiSHA1Init(&ctx);
do {
buffer_read = fread((char*)&buffer[0], 1, SHA_BUFFER_SIZE, app);
fileread += buffer_read;
swiSHA1Update(&ctx, buffer, buffer_read);
printProgressBar((float)fileread / (float)filesize);
}
while (buffer_read == SHA_BUFFER_SIZE);
clearProgressBar();
consoleSelect(&bottomScreen);
swiSHA1Final(buffer, &ctx);
//Store SHA1 sum
memcpy((tmd + 0x1F4), buffer, SHA_DIGEST_LENGTH);
}
}
int maketmd(char* input, char* tmdPath)
{
iprintf("MakeTMD for DSiWare Homebrew\n");
iprintf("by Przemyslaw Skryjomski\n\t(Tuxality)\n");
if (input == NULL || tmdPath == NULL)
{
iprintf("\x1B[33m"); //yellow
iprintf("\nUsage: %s file.app <file.tmd>\n", "maketmd");
iprintf("\x1B[47m"); //white
return 1;
}
// APP file (input)
FILE* app = fopen(input, "rb");
if (!app)
{
iprintf("\x1B[31m"); //red
iprintf("Error at opening %s for reading.\n", input);
iprintf("\x1B[47m"); //white
return 1;
}
// TMD file (output)
FILE* tmd = fopen(tmdPath, "wb");
if (!tmd)
{
fclose(app);
iprintf("\x1B[31m"); //white
iprintf("Error at opening %s for writing.\n", tmdPath);
iprintf("\x1B[47m"); //white
return 1;
}
// Allocate memory for TMD
uint8_t* tmd_template = (uint8_t*)malloc(sizeof(uint8_t) * TMD_SIZE);
memset(tmd_template, 0, sizeof(uint8_t) * TMD_SIZE); // zeroed
// Prepare TMD template then write to file
tmd_create(tmd_template, app);
fwrite((const char*)(&tmd_template[0]), TMD_SIZE, 1, tmd);
// Free allocated memory for TMD
free(tmd_template);
// This is done in dtor, but we additionally flush tmd.
fclose(app);
fclose(tmd);
return 0;
}

9
arm9/src/maketmd.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef MAKETMD_H
#define MAKETMD_H
#include "main.h"
#include "storage.h"
int maketmd(char* input, char* tmdPath);
#endif

227
arm9/src/menu.c Normal file
View File

@ -0,0 +1,227 @@
#include "menu.h"
#include "main.h"
Menu* newMenu()
{
Menu* m = (Menu*)malloc(sizeof(Menu));
m->cursor = 0;
m->page = 0;
m->itemCount = 0;
m->nextPage = false;
m->changePage = 0;
m->header[0] = '\0';
for (int i = 0; i < ITEMS_PER_PAGE; i++)
{
m->items[i].directory = false;
m->items[i].label = NULL;
m->items[i].value = NULL;
}
return m;
}
void freeMenu(Menu* m)
{
if (!m) return;
clearMenu(m);
free(m);
m = NULL;
}
void addMenuItem(Menu* m, char const* label, char const* value, bool directory)
{
if (!m) return;
int i = m->itemCount;
if (i >= ITEMS_PER_PAGE) return;
m->items[i].directory = directory;
if (label)
{
m->items[i].label = (char*)malloc(32);
sprintf(m->items[i].label, "%.31s", label);
}
if (value)
{
m->items[i].value = (char*)malloc(strlen(value)+1);
sprintf(m->items[i].value, "%s", value);
}
m->itemCount += 1;
}
static int alphabeticalCompare(const void* a, const void* b)
{
const Item* itemA = (const Item*)a;
const Item* itemB = (const Item*)b;
if (itemA->directory && !itemB->directory)
return -1;
else if (!itemA->directory && itemB->directory)
return 1;
else
return strcasecmp(itemA->label, itemB->label);
}
void sortMenuItems(Menu* m)
{
qsort(m->items, m->itemCount, sizeof(Item), alphabeticalCompare);
}
void setMenuHeader(Menu* m, char* str)
{
if (!m) return;
if (!str)
{
m->header[0] = '\0';
return;
}
char* strPtr = str;
if (strlen(strPtr) > 30)
strPtr = str + (strlen(strPtr) - 30);
sprintf(m->header, "%.30s", strPtr);
}
void resetMenu(Menu* m)
{
m->cursor = 0;
m->page = 0;
m->changePage = 0;
m->nextPage = 0;
}
void clearMenu(Menu* m)
{
if (!m) return;
for (int i = 0; i < ITEMS_PER_PAGE; i++)
{
if (m->items[i].label)
{
free(m->items[i].label);
m->items[i].label = NULL;
}
if (m->items[i].value)
{
free(m->items[i].value);
m->items[i].value = NULL;
}
}
m->itemCount = 0;
}
void printMenu(Menu* m)
{
clearScreen(&bottomScreen);
if (!m) return;
//header
iprintf("\x1B[42m"); //green
iprintf("%.30s\n\n", m->header);
iprintf("\x1B[47m"); //white
if (m->itemCount <= 0)
{
iprintf("Back - [B]\n");
return;
}
//items
for (int i = 0; i < m->itemCount; i++)
{
if (m->items[i].label)
{
if (m->items[i].directory)
iprintf(" [%.28s]\n", m->items[i].label);
else
iprintf(" %.30s\n", m->items[i].label);
}
else
iprintf(" \n");
}
//cursor
iprintf("\x1b[%d;0H>", 2 + m->cursor);
//scroll arrows
if (m->page > 0)
iprintf("\x1b[2;31H^");
if (m->nextPage)
iprintf("\x1b[21;31Hv");
}
static void _moveCursor(Menu* m, int dir)
{
if (m->changePage != 0)
return;
m->cursor += sign(dir);
if (m->cursor < 0)
{
if (m->page <= 0)
m->cursor = 0;
else
{
m->changePage = -1;
m->cursor = ITEMS_PER_PAGE - 1;
}
}
else if (m->cursor > m->itemCount-1)
{
if (m->nextPage && m->cursor >= ITEMS_PER_PAGE)
{
m->changePage = 1;
m->cursor = 0;
}
else
{
m->cursor = m->itemCount-1;
}
}
}
bool moveCursor(Menu* m)
{
if (!m) return false;
m->changePage = 0;
int lastCursor = m->cursor;
u32 down = keysDownRepeat();
if (down & KEY_DOWN)
_moveCursor(m, 1);
else if (down & KEY_UP)
_moveCursor(m, -1);
if (down & KEY_RIGHT)
{
repeat(10)
_moveCursor(m, 1);
}
else if (down & KEY_LEFT)
{
repeat(10)
_moveCursor(m, -1);
}
return !(lastCursor == m->cursor);
}

37
arm9/src/menu.h Normal file
View File

@ -0,0 +1,37 @@
#ifndef MENU_H
#define MENU_H
#include <nds/ndstypes.h>
#define ITEMS_PER_PAGE 20
typedef struct {
bool directory;
char* label;
char* value;
} Item;
typedef struct {
int cursor;
int page;
int itemCount;
bool nextPage;
int changePage;
char header[32];
Item items[ITEMS_PER_PAGE];
} Menu;
Menu* newMenu();
void freeMenu(Menu* m);
void addMenuItem(Menu* m, char const* label, char const* value, bool directory);
void sortMenuItems(Menu* m);
void setMenuHeader(Menu* m, char* str);
void resetMenu(Menu* m);
void clearMenu(Menu* m);
void printMenu(Menu* m);
bool moveCursor(Menu* m);
#endif

161
arm9/src/message.c Normal file
View File

@ -0,0 +1,161 @@
#include "message.h"
#include "main.h"
void keyWait(u32 key)
{
while (!programEnd)
{
swiWaitForVBlank();
scanKeys();
if (keysDown() & key)
break;
}
}
bool choiceBox(char* message)
{
const int choiceRow = 10;
int cursor = 0;
clearScreen(&bottomScreen);
iprintf("\x1B[33m"); //yellow
iprintf("%s\n", message);
iprintf("\x1B[47m"); //white
iprintf("\x1b[%d;0H\tYes\n\tNo\n", choiceRow);
while (!programEnd)
{
swiWaitForVBlank();
scanKeys();
//Clear cursor
iprintf("\x1b[%d;0H ", choiceRow + cursor);
if (keysDown() & (KEY_UP | KEY_DOWN))
cursor = !cursor;
//Print cursor
iprintf("\x1b[%d;0H>", choiceRow + cursor);
if (keysDown() & (KEY_A | KEY_START))
break;
if (keysDown() & KEY_B)
{
cursor = 1;
break;
}
}
scanKeys();
return (cursor == 0)? YES: NO;
}
bool choicePrint(char* message)
{
bool choice = NO;
iprintf("\x1B[33m"); //yellow
iprintf("\n%s\n", message);
iprintf("\x1B[47m"); //white
iprintf("Yes - [A]\nNo - [B]\n");
while (!programEnd)
{
swiWaitForVBlank();
scanKeys();
if (keysDown() & KEY_A)
{
choice = YES;
break;
}
else if (keysDown() & KEY_B)
{
choice = NO;
break;
}
}
scanKeys();
return choice;
}
const static u16 keys[] = {KEY_UP, KEY_DOWN, KEY_RIGHT, KEY_LEFT, KEY_A, KEY_B, KEY_X, KEY_Y};
const static char *keysLabels[] = {"\x18", "\x19", "\x1A", "\x1B", "<A>", "<B>", "<X>", "<Y>"};
bool randomConfirmBox(char* message)
{
const int choiceRow = 10;
int sequencePosition = 0;
u8 sequence[8];
for (int i = 0; i < sizeof(sequence); i++)
{
sequence[i] = rand() % (sizeof(keys) / sizeof(keys[0]));
}
clearScreen(&bottomScreen);
iprintf("\x1B[43m"); //yellow
iprintf("%s\n", message);
iprintf("\x1B[47m"); //white
iprintf("\n<START> cancel\n");
while (!programEnd && sequencePosition < sizeof(sequence))
{
swiWaitForVBlank();
scanKeys();
//Print sequence
iprintf("\x1b[%d;0H", choiceRow);
for (int i = 0; i < sizeof(sequence); i++)
{
iprintf("\x1B[%0om", i < sequencePosition ? 032 : 047);
iprintf("%s ", keysLabels[sequence[i]]);
}
if (keysDown() & (KEY_UP | KEY_DOWN | KEY_RIGHT | KEY_LEFT | KEY_A | KEY_B | KEY_X | KEY_Y))
{
if (keysDown() & keys[sequence[sequencePosition]])
sequencePosition++;
else
sequencePosition = 0;
}
if (keysDown() & KEY_START)
{
sequencePosition = 0;
break;
}
}
scanKeys();
return sequencePosition == sizeof(sequence);
}
void messageBox(char* message)
{
clearScreen(&bottomScreen);
messagePrint(message);
}
void messagePrint(char* message)
{
iprintf("%s\n", message);
iprintf("\nOkay - [A]\n");
while (!programEnd)
{
swiWaitForVBlank();
scanKeys();
if (keysDown() & (KEY_A | KEY_B | KEY_START))
break;
}
scanKeys();
}

18
arm9/src/message.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef MESSAGE_H
#define MESSAGE_H
#include <nds/ndstypes.h>
enum {
YES = true,
NO = false
};
void keyWait(u32 key);
bool choiceBox(char* message);
bool choicePrint(char* message);
bool randomConfirmBox(char* message);
void messageBox(char* message);
void messagePrint(char* message);
#endif

138
arm9/src/nand/crypto.c Normal file
View File

@ -0,0 +1,138 @@
#include <stdint.h>
#include "crypto.h"
#include "u128_math.h"
#include "f_xy.h"
#include "twltool/dsi.h"
// more info:
// https://github.com/Jimmy-Z/TWLbf/blob/master/dsi.c
// https://github.com/Jimmy-Z/bfCL/blob/master/dsi.h
// ported back to 32 bit for ARM9
static dsi_context nand_ctx;
static dsi_context boot2_ctx;
static dsi_es_context es_ctx;
static uint8_t nand_ctr_iv[16];
static uint8_t boot2_ctr[16];
static void generate_key(uint8_t *generated_key, const uint32_t *console_id, const key_mode_t mode)
{
uint32_t key[4];
switch (mode)
{
case NAND:
key[0] = console_id[0];
key[1] = console_id[0] ^ KEYSEED_DSI_NAND_0;
key[2] = console_id[1] ^ KEYSEED_DSI_NAND_1;
key[3] = console_id[1];
break;
case NAND_3DS:
key[0] = (console_id[0] ^ KEYSEED_3DS_NAND_0) | 0x80000000;
key[1] = KEYSEED_3DS_NAND_1;
key[2] = KEYSEED_3DS_NAND_2;
key[3] = console_id[1] ^ KEYSEED_3DS_NAND_3;
break;
case ES:
key[0] = KEYSEED_ES_0;
key[1] = KEYSEED_ES_1;
key[2] = console_id[1] ^ KEYSEED_ES_2;
key[3] = console_id[0];
break;
default:
break;
}
u128_xor((uint8_t *)key, mode == ES ? DSi_ES_KEY_Y : DSi_NAND_KEY_Y);
u128_add((uint8_t *)key, DSi_KEY_MAGIC);
u128_lrot((uint8_t *)key, 42);
memcpy(generated_key, key, 16);
}
int dsi_sha1_verify(const void *digest_verify, const void *data, unsigned len)
{
uint8_t digest[SHA1_LEN];
swiSHA1Calc(digest, data, len);
return memcmp(digest, digest_verify, SHA1_LEN);
}
void dsi_crypt_init(const uint8_t *console_id_be, const uint8_t *emmc_cid, int is3DS)
{
uint32_t console_id[2];
GET_UINT32_BE(console_id[0], console_id_be, 4);
GET_UINT32_BE(console_id[1], console_id_be, 0);
uint8_t key[16];
generate_key(key, console_id, is3DS ? NAND_3DS : NAND);
dsi_set_key(&nand_ctx, key);
u32 normalkey[4];
u32 tadsrl_keyX[4] = {0x4E00004A, 0x4A00004E, 0, 0};
tadsrl_keyX[2] = console_id[1] ^ 0xC80C4B72;
tadsrl_keyX[3] = console_id[0];
F_XY((u8 *)normalkey, (u8 *)tadsrl_keyX, DSi_ES_KEY_Y);
dsi_es_init(&es_ctx, (u8*)normalkey);
dsi_set_key(&boot2_ctx, DSi_BOOT2_KEY);
swiSHA1Calc(nand_ctr_iv, emmc_cid, 16);
}
// crypt one block, in/out must be aligned to 32 bit(restriction induced by xor_128)
// offset as block offset, block as AES block
void dsi_nand_crypt_1(uint8_t* out, const uint8_t* in, uint32_t offset)
{
uint8_t ctr[16];
memcpy(ctr, nand_ctr_iv, sizeof(nand_ctr_iv));
u128_add32(ctr, offset);
dsi_set_ctr(&nand_ctx, ctr);
dsi_crypt_ctr(&nand_ctx, in, out, 16);
}
void dsi_nand_crypt(uint8_t* out, const uint8_t* in, uint32_t offset, unsigned count)
{
uint8_t ctr[16];
memcpy(ctr, nand_ctr_iv, sizeof(nand_ctr_iv));
u128_add32(ctr, offset);
for (unsigned i = 0; i < count; ++i)
{
dsi_set_ctr(&nand_ctx, ctr);
dsi_crypt_ctr(&nand_ctx, in, out, 16);
out += AES_BLOCK_SIZE;
in += AES_BLOCK_SIZE;
u128_add32(ctr, 1);
}
}
int dsi_es_block_crypt(uint8_t *buf, unsigned buf_len, crypt_mode_t mode)
{
if (mode == DECRYPT)
return dsi_es_decrypt(&es_ctx, buf, buf + buf_len - 0x20, buf_len - 0x20);
else
dsi_es_encrypt(&es_ctx, buf, buf + buf_len - 0x20, buf_len - 0x20);
return 0;
}
void dsi_boot2_crypt_set_ctr(uint32_t size_r)
{
for (int i=0;i<4;i++)
{
boot2_ctr[i] = (size_r) >> (8*i);
boot2_ctr[i+4] = (-size_r) >> (8*i);
boot2_ctr[i+8] = (~size_r) >> (8*i);
boot2_ctr[i+12] = 0;
}
}
void dsi_boot2_crypt(uint8_t* out, const uint8_t* in, unsigned count)
{
for (unsigned i = 0; i < count; ++i)
{
dsi_set_ctr(&boot2_ctx, boot2_ctr);
dsi_crypt_ctr(&boot2_ctx, in, out, 16);
out += AES_BLOCK_SIZE;
in += AES_BLOCK_SIZE;
u128_add32(boot2_ctr, 1);
}
}

70
arm9/src/nand/crypto.h Normal file
View File

@ -0,0 +1,70 @@
#pragma once
#include <nds.h>
#ifdef __cplusplus
extern "C" {
#endif
/************************ Constants / Defines *********************************/
#define AES_BLOCK_SIZE 16
#define SHA1_LEN 20
#define KEYSEED_DSI_NAND_0 0x24ee6906
#define KEYSEED_DSI_NAND_1 0xe65b601d
#define KEYSEED_3DS_NAND_0 0xb358a6af
#define KEYSEED_3DS_NAND_1 0x544e494e
#define KEYSEED_3DS_NAND_2 0x4f444e45
#define KEYSEED_3DS_NAND_3 0x08c267b7
#define KEYSEED_ES_0 0x4e00004a
#define KEYSEED_ES_1 0x4a00004e
#define KEYSEED_ES_2 0xc80c4b72
#define GET_UINT32_BE(n, b, i) \
((uint8_t*)&(n))[0] = (b)[i + 3]; \
((uint8_t*)&(n))[1] = (b)[i + 2]; \
((uint8_t*)&(n))[2] = (b)[i + 1]; \
((uint8_t*)&(n))[3] = (b)[i + 0]
#define PUT_UINT32_BE(n, b, i) \
(b)[i + 0] = ((uint8_t*)&(n))[3]; \
(b)[i + 1] = ((uint8_t*)&(n))[2]; \
(b)[i + 2] = ((uint8_t*)&(n))[1]; \
(b)[i + 3] = ((uint8_t*)&(n))[0]
typedef enum {
ENCRYPT,
DECRYPT
} crypt_mode_t;
typedef enum {
NAND,
NAND_3DS,
ES
} key_mode_t;
// don't want to include nds.h just for this
void swiSHA1Calc(void *digest, const void *buf, size_t len);
int dsi_sha1_verify(const void *digest_verify, const void *data, unsigned len);
void dsi_crypt_init(const uint8_t *console_id_be, const uint8_t *emmc_cid, int is3DS);
void dsi_nand_crypt_1(uint8_t *out, const uint8_t* in, u32 offset);
void dsi_nand_crypt(uint8_t *out, const uint8_t* in, u32 offset, unsigned count);
int dsi_es_block_crypt(uint8_t *buf, unsigned buf_len, crypt_mode_t mode);
void dsi_boot2_crypt_set_ctr(uint32_t size_r);
void dsi_boot2_crypt(uint8_t* out, const uint8_t* in, unsigned count);
#ifdef __cplusplus
}
#endif

57
arm9/src/nand/f_xy.c Normal file
View File

@ -0,0 +1,57 @@
/* f_xy.c
*
* This file was imported from godmode9i, but it is liely not the
* original source. twltool uses the same file.
*
* If you happen to know whom to credit I'd love to add the name
*
* Refactored to reduce the pointer casts and remove the dependency
* from tonccpy.
*/
#include <string.h>
#include <stdint.h>
#include "u128_math.h"
/************************ Constants / Defines *********************************/
const uint8_t DSi_KEY_MAGIC[16] = {
0x79, 0x3e, 0x4f, 0x1a, 0x5f, 0x0f, 0x68, 0x2a,
0x58, 0x02, 0x59, 0x29, 0x4e, 0xfb, 0xfe, 0xff
};
const uint8_t DSi_NAND_KEY_Y[16] = {
0x76, 0xdc, 0xb9, 0x0a, 0xd3, 0xc4, 0x4d, 0xbd,
0x1d, 0xdd, 0x2d, 0x20, 0x05, 0x00, 0xa0, 0xe1
};
const uint8_t DSi_ES_KEY_Y[16] = {
0xe5, 0xcc, 0x5a, 0x8b, 0x56, 0xd0, 0xc9,0x72,
0x9c, 0x17, 0xe8, 0xdc, 0x39, 0x12, 0x36, 0xa9
};
const uint8_t DSi_BOOT2_KEY[16] = {
0x98, 0xee, 0x80, 0x80, 0x00, 0x6c, 0xb4, 0xf6,
0x3a, 0xc2, 0x6e, 0x62, 0xf9, 0xec, 0x34, 0xad
};
/************************ Functions *******************************************/
void F_XY(uint8_t *key, const uint8_t *key_x, const uint8_t *key_y)
{
uint8_t key_xy[16];
for (int i=0; i<16; i++)
key_xy[i] = key_x[i] ^ key_y[i];
memcpy(key, DSi_KEY_MAGIC, sizeof(DSi_KEY_MAGIC));
u128_add(key, key_xy);
u128_lrot(key, 42);
}
//F_XY_reverse does the reverse of F(X^Y): takes (normal)key, and does F in reverse to generate the original X^Y key_xy.
void F_XY_reverse(const uint8_t *key, uint8_t *key_xy)
{
memcpy(key_xy, key, 16);
u128_rrot(key_xy, 42);
u128_sub(key_xy, DSi_KEY_MAGIC);
}

35
arm9/src/nand/f_xy.h Normal file
View File

@ -0,0 +1,35 @@
/* f_xy.h
*
* This file was imported from godmode9i, but it is liely not the
* original source. twltool uses the same file.
*
* If you happen to know whom to credit I'd love to add the name
*
* Refactored to reduce the pointer casts and remove the dependency
* from tonccpy.
*/
#ifndef _H_F_XY
#define _H_F_XY
#ifdef __cplusplus
extern "C" {
#endif
/************************ Constants / Defines *********************************/
extern const uint8_t DSi_KEY_MAGIC[16];
extern const uint8_t DSi_NAND_KEY_Y[16];
extern const uint8_t DSi_ES_KEY_Y[16];
extern const uint8_t DSi_BOOT2_KEY[16];
/************************ Function Protoypes **********************************/
void F_XY(uint8_t *key, const uint8_t *key_x, const uint8_t *key_y);
void F_XY_reverse(const uint8_t *key, uint8_t *key_xy);
#ifdef __cplusplus
}
#endif
#endif

269
arm9/src/nand/nandio.c Normal file
View File

@ -0,0 +1,269 @@
#include <nds.h>
#include <nds/disc_io.h>
#include <malloc.h>
#include <stdio.h>
#include "crypto.h"
#include "sector0.h"
#include "f_xy.h"
#include "../message.h"
#include "nandio.h"
#include "u128_math.h"
/************************ Function Protoypes **********************************/
bool nandio_startup();
bool nandio_is_inserted();
bool nandio_read_sectors(sec_t offset, sec_t len, void *buffer);
bool nandio_write_sectors(sec_t offset, sec_t len, const void *buffer);
bool nandio_clear_status();
bool nandio_shutdown();
/************************ Constants / Defines *********************************/
const DISC_INTERFACE io_dsi_nand = {
NAND_DEVICENAME,
FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE,
nandio_startup,
nandio_is_inserted,
nandio_read_sectors,
nandio_write_sectors,
nandio_clear_status,
nandio_shutdown
};
bool is3DS;
static bool writingLocked = true;
static bool nandWritten = false;
extern bool nand_Startup();
static u8* crypt_buf = 0;
static u32 fat_sig_fix_offset = 0;
static u32 sector_buf32[SECTOR_SIZE/sizeof(u32)];
static u8 *sector_buf = (u8*)sector_buf32;
void nandio_set_fat_sig_fix(u32 offset)
{
fat_sig_fix_offset = offset;
}
void getConsoleID(u8 *consoleID)
{
u8 *fifo=(u8*)0x02300000; //shared mem address that has our computed key3 stuff
u8 key[16]; //key3 normalkey - keyslot 3 is used for DSi/twln NAND crypto
u8 key_x[16];////key3_x - contains a DSi console id (which just happens to be the LFCS on 3ds)
memcpy(key, fifo, 16); //receive the goods from arm7
F_XY_reverse(key, key_x); //work backwards from the normalkey to get key_x that has the consoleID
u128_xor(key_x, DSi_NAND_KEY_Y);
memcpy(&consoleID[0], &key_x[0], 4);
memcpy(&consoleID[4], &key_x[0xC], 4);
}
bool nandio_startup()
{
if (!nand_Startup())
{
return false;
}
nand_ReadSectors(0, 1, sector_buf);
is3DS = parse_ncsd(sector_buf) == 0;
//if (is3DS) return false;
u8 consoleID[8];
u8 consoleIDfixed[8];
// Get ConsoleID
getConsoleID(consoleID);
for (int i = 0; i < 8; i++)
{
consoleIDfixed[i] = consoleID[7-i];
}
// iprintf("sector 0 is %s\n", is3DS ? "3DS" : "DSi");
dsi_crypt_init((const u8*)consoleIDfixed, (const u8*)0x2FFD7BC, is3DS);
dsi_nand_crypt(sector_buf, sector_buf, 0, SECTOR_SIZE / AES_BLOCK_SIZE);
parse_mbr(sector_buf, is3DS);
mbr_t *mbr = (mbr_t*)sector_buf;
nandio_set_fat_sig_fix(is3DS ? 0 : mbr->partitions[0].offset);
if (crypt_buf == 0)
{
crypt_buf = (u8*)memalign(32, SECTOR_SIZE * CRYPT_BUF_LEN);
}
return crypt_buf != 0;
}
bool nandio_is_inserted()
{
return true;
}
// len is guaranteed <= CRYPT_BUF_LEN
static bool read_sectors(sec_t start, sec_t len, void *buffer)
{
if (nand_ReadSectors(start, len, crypt_buf))
{
dsi_nand_crypt(buffer, crypt_buf, start * SECTOR_SIZE / AES_BLOCK_SIZE, len * SECTOR_SIZE / AES_BLOCK_SIZE);
if (fat_sig_fix_offset &&
start == fat_sig_fix_offset
&& ((u8*)buffer)[0x36] == 0
&& ((u8*)buffer)[0x37] == 0
&& ((u8*)buffer)[0x38] == 0)
{
((u8*)buffer)[0x36] = 'F';
((u8*)buffer)[0x37] = 'A';
((u8*)buffer)[0x38] = 'T';
}
return true;
}
else
{
return false;
}
}
// len is guaranteed <= CRYPT_BUF_LEN
static bool write_sectors(sec_t start, sec_t len, const void *buffer)
{
static u8 writeCopy[SECTOR_SIZE*16];
memcpy(writeCopy, buffer, len * SECTOR_SIZE);
dsi_nand_crypt(crypt_buf, writeCopy, start * SECTOR_SIZE / AES_BLOCK_SIZE, len * SECTOR_SIZE / AES_BLOCK_SIZE);
if (nand_WriteSectors(start, len, crypt_buf))
{
return true;
}
else
{
return false;
}
}
bool nandio_read_sectors(sec_t offset, sec_t len, void *buffer)
{
while (len >= CRYPT_BUF_LEN)
{
if (!read_sectors(offset, CRYPT_BUF_LEN, buffer))
{
return false;
}
offset += CRYPT_BUF_LEN;
len -= CRYPT_BUF_LEN;
buffer = ((u8*)buffer) + SECTOR_SIZE * CRYPT_BUF_LEN;
}
if (len > 0)
{
return read_sectors(offset, len, buffer);
} else
{
return true;
}
}
bool nandio_write_sectors(sec_t offset, sec_t len, const void *buffer)
{
if (writingLocked)
return false;
nandWritten = true;
while (len >= CRYPT_BUF_LEN)
{
if (!write_sectors(offset, CRYPT_BUF_LEN, buffer))
{
return false;
}
offset += CRYPT_BUF_LEN;
len -= CRYPT_BUF_LEN;
buffer = ((u8*)buffer) + SECTOR_SIZE * CRYPT_BUF_LEN;
}
if (len > 0)
{
return write_sectors(offset, len, buffer);
} else
{
return true;
}
}
bool nandio_clear_status()
{
return true;
}
bool nandio_shutdown()
{
if (nandWritten)
{
// at cleanup we synchronize the FAT statgings
// A FatFS might have multiple copies of the FAT.
// we will get them back synchonized as we just worked on the first copy
// this allows us to revert changes in the FAT if we did not properly finish
// and did not push the changes to the other copies
// to do this we read the first partition sector
nandio_read_sectors(fat_sig_fix_offset, 1, sector_buf);
u8 stagingLevels = sector_buf[0x10];
u8 reservedSectors = sector_buf[0x0E];
u16 sectorsPerFatCopy = sector_buf[0x16] | ((u16)sector_buf[0x17] << 8);
/*
iprintf("[i] Staging for %i FAT copies\n",stagingLevels);
iprintf("[i] Stages starting at %i\n",reservedSectors);
iprintf("[i] %i sectors per stage\n",sectorsPerFatCopy);
*/
if (stagingLevels > 1)
{
for (u32 sector = 0;sector < sectorsPerFatCopy; sector++)
{
// read fat sector
nandio_read_sectors(fat_sig_fix_offset + reservedSectors + sector, 1, sector_buf);
// write to each copy, except the source copy
writingLocked = false;
for (int stage = 1;stage < stagingLevels;stage++)
{
nandio_write_sectors(fat_sig_fix_offset + reservedSectors + sector + (stage *sectorsPerFatCopy), 1, sector_buf);
}
writingLocked = true;
}
}
nandWritten = false;
}
free(crypt_buf);
crypt_buf = 0;
return true;
}
bool nandio_lock_writing()
{
writingLocked = true;
return writingLocked;
}
bool nandio_unlock_writing()
{
if (writingLocked && randomConfirmBox("Writing to NAND is locked!\nIf you're sure you understand\nthe risk, input the sequence\nbelow."))
writingLocked = false;
return !writingLocked;
}
bool nandio_force_fat_fix()
{
if (!writingLocked)
nandWritten = true;
return true;
}

31
arm9/src/nand/nandio.h Normal file
View File

@ -0,0 +1,31 @@
#pragma once
#include <stdint.h>
#include <nds/disc_io.h>
#ifdef __cplusplus
extern "C" {
#endif
/************************ Constants / Defines *********************************/
#define CRYPT_BUF_LEN 64
#define NAND_DEVICENAME (('N' << 24) | ('A' << 16) | ('N' << 8) | 'D')
extern const DISC_INTERFACE io_dsi_nand;
/************************ Function Protoypes **********************************/
void nandio_set_fat_sig_fix(uint32_t offset);
void getConsoleID(uint8_t *consoleID);
extern bool nandio_shutdown();
extern bool nandio_lock_writing();
extern bool nandio_unlock_writing();
extern bool nandio_force_fat_fix();
#ifdef __cplusplus
}
#endif

1164
arm9/src/nand/polarssl/aes.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,139 @@
/**
* \file aes.h
*
* Copyright (C) 2006-2010, Brainspark B.V.
*
* This file is part of PolarSSL (http://www.polarssl.org)
* Lead Maintainer: Paul Bakker <polarssl_maintainer at polarssl.org>
*
* All rights reserved.
*
* 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.
*
* 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, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef POLARSSL_AES_H
#define POLARSSL_AES_H
#define AES_ENCRYPT 1
#define AES_DECRYPT 0
#define POLARSSL_ERR_AES_INVALID_KEY_LENGTH -0x0800
#define POLARSSL_ERR_AES_INVALID_INPUT_LENGTH -0x0810
/**
* \brief AES context structure
*/
typedef struct
{
int nr; /*!< number of rounds */
unsigned long *rk; /*!< AES round keys */
unsigned long buf[68]; /*!< unaligned data */
}
aes_context;
#ifdef __cplusplus
extern "C" {
#endif
/**
* \brief AES key schedule (encryption)
*
* \param ctx AES context to be initialized
* \param key encryption key
* \param keysize must be 128, 192 or 256
*
* \return 0 if successful, or POLARSSL_ERR_AES_INVALID_KEY_LENGTH
*/
int aes_setkey_enc( aes_context *ctx, const unsigned char *key, int keysize );
/**
* \brief AES key schedule (decryption)
*
* \param ctx AES context to be initialized
* \param key decryption key
* \param keysize must be 128, 192 or 256
*
* \return 0 if successful, or POLARSSL_ERR_AES_INVALID_KEY_LENGTH
*/
int aes_setkey_dec( aes_context *ctx, const unsigned char *key, int keysize );
/**
* \brief AES-ECB block encryption/decryption
*
* \param ctx AES context
* \param mode AES_ENCRYPT or AES_DECRYPT
* \param input 16-byte input block
* \param output 16-byte output block
*
* \return 0 if successful
*/
int aes_crypt_ecb( aes_context *ctx,
int mode,
const unsigned char input[16],
unsigned char output[16] );
/**
* \brief AES-CBC buffer encryption/decryption
* Length should be a multiple of the block
* size (16 bytes)
*
* \param ctx AES context
* \param mode AES_ENCRYPT or AES_DECRYPT
* \param length length of the input data
* \param iv initialization vector (updated after use)
* \param input buffer holding the input data
* \param output buffer holding the output data
*
* \return 0 if successful, or POLARSSL_ERR_AES_INVALID_INPUT_LENGTH
*/
int aes_crypt_cbc( aes_context *ctx,
int mode,
int length,
unsigned char iv[16],
const unsigned char *input,
unsigned char *output );
/**
* \brief AES-CFB128 buffer encryption/decryption.
*
* \param ctx AES context
* \param mode AES_ENCRYPT or AES_DECRYPT
* \param length length of the input data
* \param iv_off offset in IV (updated after use)
* \param iv initialization vector (updated after use)
* \param input buffer holding the input data
* \param output buffer holding the output data
*
* \return 0 if successful
*/
int aes_crypt_cfb128( aes_context *ctx,
int mode,
int length,
int *iv_off,
unsigned char iv[16],
const unsigned char *input,
unsigned char *output );
/**
* \brief Checkup routine
*
* \return 0 if successful, or 1 if the test failed
*/
int aes_self_test( int verbose );
#ifdef __cplusplus
}
#endif
#endif /* aes.h */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,761 @@
/**
* \file bignum.h
*
* \brief Multi-precision integer library
*
* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* This file is part of mbed TLS (https://tls.mbed.org)
*/
#ifndef MBEDTLS_BIGNUM_H
#define MBEDTLS_BIGNUM_H
#if !defined(MBEDTLS_CONFIG_FILE)
#include "config.h"
#else
#include MBEDTLS_CONFIG_FILE
#endif
#include <stddef.h>
#include <stdint.h>
#if defined(MBEDTLS_FS_IO)
#include <stdio.h>
#endif
#define MBEDTLS_ERR_MPI_FILE_IO_ERROR -0x0002 /**< An error occurred while reading from or writing to a file. */
#define MBEDTLS_ERR_MPI_BAD_INPUT_DATA -0x0004 /**< Bad input parameters to function. */
#define MBEDTLS_ERR_MPI_INVALID_CHARACTER -0x0006 /**< There is an invalid character in the digit string. */
#define MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL -0x0008 /**< The buffer is too small to write to. */
#define MBEDTLS_ERR_MPI_NEGATIVE_VALUE -0x000A /**< The input arguments are negative or result in illegal output. */
#define MBEDTLS_ERR_MPI_DIVISION_BY_ZERO -0x000C /**< The input argument for division is zero, which is not allowed. */
#define MBEDTLS_ERR_MPI_NOT_ACCEPTABLE -0x000E /**< The input arguments are not acceptable. */
#define MBEDTLS_ERR_MPI_ALLOC_FAILED -0x0010 /**< Memory allocation failed. */
#define MBEDTLS_MPI_CHK(f) do { if( ( ret = f ) != 0 ) goto cleanup; } while( 0 )
/*
* Maximum size MPIs are allowed to grow to in number of limbs.
*/
#define MBEDTLS_MPI_MAX_LIMBS 10000
#if !defined(MBEDTLS_MPI_WINDOW_SIZE)
/*
* Maximum window size used for modular exponentiation. Default: 6
* Minimum value: 1. Maximum value: 6.
*
* Result is an array of ( 2 << MBEDTLS_MPI_WINDOW_SIZE ) MPIs used
* for the sliding window calculation. (So 64 by default)
*
* Reduction in size, reduces speed.
*/
#define MBEDTLS_MPI_WINDOW_SIZE 6 /**< Maximum windows size used. */
#endif /* !MBEDTLS_MPI_WINDOW_SIZE */
#if !defined(MBEDTLS_MPI_MAX_SIZE)
/*
* Maximum size of MPIs allowed in bits and bytes for user-MPIs.
* ( Default: 512 bytes => 4096 bits, Maximum tested: 2048 bytes => 16384 bits )
*
* Note: Calculations can results temporarily in larger MPIs. So the number
* of limbs required (MBEDTLS_MPI_MAX_LIMBS) is higher.
*/
#define MBEDTLS_MPI_MAX_SIZE 1024 /**< Maximum number of bytes for usable MPIs. */
#endif /* !MBEDTLS_MPI_MAX_SIZE */
#define MBEDTLS_MPI_MAX_BITS ( 8 * MBEDTLS_MPI_MAX_SIZE ) /**< Maximum number of bits for usable MPIs. */
/*
* When reading from files with mbedtls_mpi_read_file() and writing to files with
* mbedtls_mpi_write_file() the buffer should have space
* for a (short) label, the MPI (in the provided radix), the newline
* characters and the '\0'.
*
* By default we assume at least a 10 char label, a minimum radix of 10
* (decimal) and a maximum of 4096 bit numbers (1234 decimal chars).
* Autosized at compile time for at least a 10 char label, a minimum radix
* of 10 (decimal) for a number of MBEDTLS_MPI_MAX_BITS size.
*
* This used to be statically sized to 1250 for a maximum of 4096 bit
* numbers (1234 decimal chars).
*
* Calculate using the formula:
* MBEDTLS_MPI_RW_BUFFER_SIZE = ceil(MBEDTLS_MPI_MAX_BITS / ln(10) * ln(2)) +
* LabelSize + 6
*/
#define MBEDTLS_MPI_MAX_BITS_SCALE100 ( 100 * MBEDTLS_MPI_MAX_BITS )
#define MBEDTLS_LN_2_DIV_LN_10_SCALE100 332
#define MBEDTLS_MPI_RW_BUFFER_SIZE ( ((MBEDTLS_MPI_MAX_BITS_SCALE100 + MBEDTLS_LN_2_DIV_LN_10_SCALE100 - 1) / MBEDTLS_LN_2_DIV_LN_10_SCALE100) + 10 + 6 )
/*
* Define the base integer type, architecture-wise.
*
* 32 or 64-bit integer types can be forced regardless of the underlying
* architecture by defining MBEDTLS_HAVE_INT32 or MBEDTLS_HAVE_INT64
* respectively and undefining MBEDTLS_HAVE_ASM.
*
* Double-width integers (e.g. 128-bit in 64-bit architectures) can be
* disabled by defining MBEDTLS_NO_UDBL_DIVISION.
*/
#if !defined(MBEDTLS_HAVE_INT32)
#if defined(_MSC_VER) && defined(_M_AMD64)
/* Always choose 64-bit when using MSC */
#if !defined(MBEDTLS_HAVE_INT64)
#define MBEDTLS_HAVE_INT64
#endif /* !MBEDTLS_HAVE_INT64 */
typedef int64_t mbedtls_mpi_sint;
typedef uint64_t mbedtls_mpi_uint;
#elif defined(__GNUC__) && ( \
defined(__amd64__) || defined(__x86_64__) || \
defined(__ppc64__) || defined(__powerpc64__) || \
defined(__ia64__) || defined(__alpha__) || \
( defined(__sparc__) && defined(__arch64__) ) || \
defined(__s390x__) || defined(__mips64) )
#if !defined(MBEDTLS_HAVE_INT64)
#define MBEDTLS_HAVE_INT64
#endif /* MBEDTLS_HAVE_INT64 */
typedef int64_t mbedtls_mpi_sint;
typedef uint64_t mbedtls_mpi_uint;
#if !defined(MBEDTLS_NO_UDBL_DIVISION)
/* mbedtls_t_udbl defined as 128-bit unsigned int */
typedef unsigned int mbedtls_t_udbl __attribute__((mode(TI)));
#define MBEDTLS_HAVE_UDBL
#endif /* !MBEDTLS_NO_UDBL_DIVISION */
#elif defined(__ARMCC_VERSION) && defined(__aarch64__)
/*
* __ARMCC_VERSION is defined for both armcc and armclang and
* __aarch64__ is only defined by armclang when compiling 64-bit code
*/
#if !defined(MBEDTLS_HAVE_INT64)
#define MBEDTLS_HAVE_INT64
#endif /* !MBEDTLS_HAVE_INT64 */
typedef int64_t mbedtls_mpi_sint;
typedef uint64_t mbedtls_mpi_uint;
#if !defined(MBEDTLS_NO_UDBL_DIVISION)
/* mbedtls_t_udbl defined as 128-bit unsigned int */
typedef __uint128_t mbedtls_t_udbl;
#define MBEDTLS_HAVE_UDBL
#endif /* !MBEDTLS_NO_UDBL_DIVISION */
#elif defined(MBEDTLS_HAVE_INT64)
/* Force 64-bit integers with unknown compiler */
typedef int64_t mbedtls_mpi_sint;
typedef uint64_t mbedtls_mpi_uint;
#endif
#endif /* !MBEDTLS_HAVE_INT32 */
#if !defined(MBEDTLS_HAVE_INT64)
/* Default to 32-bit compilation */
#if !defined(MBEDTLS_HAVE_INT32)
#define MBEDTLS_HAVE_INT32
#endif /* !MBEDTLS_HAVE_INT32 */
typedef int32_t mbedtls_mpi_sint;
typedef uint32_t mbedtls_mpi_uint;
#if !defined(MBEDTLS_NO_UDBL_DIVISION)
typedef uint64_t mbedtls_t_udbl;
#define MBEDTLS_HAVE_UDBL
#endif /* !MBEDTLS_NO_UDBL_DIVISION */
#endif /* !MBEDTLS_HAVE_INT64 */
#ifdef __cplusplus
extern "C" {
#endif
/**
* \brief MPI structure
*/
typedef struct
{
int s; /*!< integer sign */
size_t n; /*!< total # of limbs */
mbedtls_mpi_uint *p; /*!< pointer to limbs */
}
mbedtls_mpi;
/**
* \brief Initialize one MPI (make internal references valid)
* This just makes it ready to be set or freed,
* but does not define a value for the MPI.
*
* \param X One MPI to initialize.
*/
void mbedtls_mpi_init( mbedtls_mpi *X );
/**
* \brief Unallocate one MPI
*
* \param X One MPI to unallocate.
*/
void mbedtls_mpi_free( mbedtls_mpi *X );
/**
* \brief Enlarge to the specified number of limbs
*
* \param X MPI to grow
* \param nblimbs The target number of limbs
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed
*/
int mbedtls_mpi_grow( mbedtls_mpi *X, size_t nblimbs );
/**
* \brief Resize down, keeping at least the specified number of limbs
*
* \param X MPI to shrink
* \param nblimbs The minimum number of limbs to keep
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed
*/
int mbedtls_mpi_shrink( mbedtls_mpi *X, size_t nblimbs );
/**
* \brief Copy the contents of Y into X
*
* \param X Destination MPI
* \param Y Source MPI
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed
*/
int mbedtls_mpi_copy( mbedtls_mpi *X, const mbedtls_mpi *Y );
/**
* \brief Swap the contents of X and Y
*
* \param X First MPI value
* \param Y Second MPI value
*/
void mbedtls_mpi_swap( mbedtls_mpi *X, mbedtls_mpi *Y );
/**
* \brief Safe conditional assignement X = Y if assign is 1
*
* \param X MPI to conditionally assign to
* \param Y Value to be assigned
* \param assign 1: perform the assignment, 0: keep X's original value
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed,
*
* \note This function is equivalent to
* if( assign ) mbedtls_mpi_copy( X, Y );
* except that it avoids leaking any information about whether
* the assignment was done or not (the above code may leak
* information through branch prediction and/or memory access
* patterns analysis).
*/
int mbedtls_mpi_safe_cond_assign( mbedtls_mpi *X, const mbedtls_mpi *Y, unsigned char assign );
/**
* \brief Safe conditional swap X <-> Y if swap is 1
*
* \param X First mbedtls_mpi value
* \param Y Second mbedtls_mpi value
* \param assign 1: perform the swap, 0: keep X and Y's original values
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed,
*
* \note This function is equivalent to
* if( assign ) mbedtls_mpi_swap( X, Y );
* except that it avoids leaking any information about whether
* the assignment was done or not (the above code may leak
* information through branch prediction and/or memory access
* patterns analysis).
*/
int mbedtls_mpi_safe_cond_swap( mbedtls_mpi *X, mbedtls_mpi *Y, unsigned char assign );
/**
* \brief Set value from integer
*
* \param X MPI to set
* \param z Value to use
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed
*/
int mbedtls_mpi_lset( mbedtls_mpi *X, mbedtls_mpi_sint z );
/**
* \brief Get a specific bit from X
*
* \param X MPI to use
* \param pos Zero-based index of the bit in X
*
* \return Either a 0 or a 1
*/
int mbedtls_mpi_get_bit( const mbedtls_mpi *X, size_t pos );
/**
* \brief Set a bit of X to a specific value of 0 or 1
*
* \note Will grow X if necessary to set a bit to 1 in a not yet
* existing limb. Will not grow if bit should be set to 0
*
* \param X MPI to use
* \param pos Zero-based index of the bit in X
* \param val The value to set the bit to (0 or 1)
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed,
* MBEDTLS_ERR_MPI_BAD_INPUT_DATA if val is not 0 or 1
*/
int mbedtls_mpi_set_bit( mbedtls_mpi *X, size_t pos, unsigned char val );
/**
* \brief Return the number of zero-bits before the least significant
* '1' bit
*
* Note: Thus also the zero-based index of the least significant '1' bit
*
* \param X MPI to use
*/
size_t mbedtls_mpi_lsb( const mbedtls_mpi *X );
/**
* \brief Return the number of bits up to and including the most
* significant '1' bit'
*
* Note: Thus also the one-based index of the most significant '1' bit
*
* \param X MPI to use
*/
size_t mbedtls_mpi_bitlen( const mbedtls_mpi *X );
/**
* \brief Return the total size in bytes
*
* \param X MPI to use
*/
size_t mbedtls_mpi_size( const mbedtls_mpi *X );
/**
* \brief Import from an ASCII string
*
* \param X Destination MPI
* \param radix Input numeric base
* \param s Null-terminated string buffer
*
* \return 0 if successful, or a MBEDTLS_ERR_MPI_XXX error code
*/
int mbedtls_mpi_read_string( mbedtls_mpi *X, int radix, const char *s );
/**
* \brief Export into an ASCII string
*
* \param X Source MPI
* \param radix Output numeric base
* \param buf Buffer to write the string to
* \param buflen Length of buf
* \param olen Length of the string written, including final NUL byte
*
* \return 0 if successful, or a MBEDTLS_ERR_MPI_XXX error code.
* *olen is always updated to reflect the amount
* of data that has (or would have) been written.
*
* \note Call this function with buflen = 0 to obtain the
* minimum required buffer size in *olen.
*/
int mbedtls_mpi_write_string( const mbedtls_mpi *X, int radix,
char *buf, size_t buflen, size_t *olen );
#if defined(MBEDTLS_FS_IO)
/**
* \brief Read MPI from a line in an opened file
*
* \param X Destination MPI
* \param radix Input numeric base
* \param fin Input file handle
*
* \return 0 if successful, MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL if
* the file read buffer is too small or a
* MBEDTLS_ERR_MPI_XXX error code
*
* \note On success, this function advances the file stream
* to the end of the current line or to EOF.
*
* The function returns 0 on an empty line.
*
* Leading whitespaces are ignored, as is a
* '0x' prefix for radix 16.
*
*/
int mbedtls_mpi_read_file( mbedtls_mpi *X, int radix, FILE *fin );
/**
* \brief Write X into an opened file, or stdout if fout is NULL
*
* \param p Prefix, can be NULL
* \param X Source MPI
* \param radix Output numeric base
* \param fout Output file handle (can be NULL)
*
* \return 0 if successful, or a MBEDTLS_ERR_MPI_XXX error code
*
* \note Set fout == NULL to print X on the console.
*/
int mbedtls_mpi_write_file( const char *p, const mbedtls_mpi *X, int radix, FILE *fout );
#endif /* MBEDTLS_FS_IO */
/**
* \brief Import X from unsigned binary data, big endian
*
* \param X Destination MPI
* \param buf Input buffer
* \param buflen Input buffer size
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed
*/
int mbedtls_mpi_read_binary( mbedtls_mpi *X, const unsigned char *buf, size_t buflen );
/**
* \brief Export X into unsigned binary data, big endian.
* Always fills the whole buffer, which will start with zeros
* if the number is smaller.
*
* \param X Source MPI
* \param buf Output buffer
* \param buflen Output buffer size
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL if buf isn't large enough
*/
int mbedtls_mpi_write_binary( const mbedtls_mpi *X, unsigned char *buf, size_t buflen );
/**
* \brief Left-shift: X <<= count
*
* \param X MPI to shift
* \param count Amount to shift
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed
*/
int mbedtls_mpi_shift_l( mbedtls_mpi *X, size_t count );
/**
* \brief Right-shift: X >>= count
*
* \param X MPI to shift
* \param count Amount to shift
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed
*/
int mbedtls_mpi_shift_r( mbedtls_mpi *X, size_t count );
/**
* \brief Compare unsigned values
*
* \param X Left-hand MPI
* \param Y Right-hand MPI
*
* \return 1 if |X| is greater than |Y|,
* -1 if |X| is lesser than |Y| or
* 0 if |X| is equal to |Y|
*/
int mbedtls_mpi_cmp_abs( const mbedtls_mpi *X, const mbedtls_mpi *Y );
/**
* \brief Compare signed values
*
* \param X Left-hand MPI
* \param Y Right-hand MPI
*
* \return 1 if X is greater than Y,
* -1 if X is lesser than Y or
* 0 if X is equal to Y
*/
int mbedtls_mpi_cmp_mpi( const mbedtls_mpi *X, const mbedtls_mpi *Y );
/**
* \brief Compare signed values
*
* \param X Left-hand MPI
* \param z The integer value to compare to
*
* \return 1 if X is greater than z,
* -1 if X is lesser than z or
* 0 if X is equal to z
*/
int mbedtls_mpi_cmp_int( const mbedtls_mpi *X, mbedtls_mpi_sint z );
/**
* \brief Unsigned addition: X = |A| + |B|
*
* \param X Destination MPI
* \param A Left-hand MPI
* \param B Right-hand MPI
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed
*/
int mbedtls_mpi_add_abs( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B );
/**
* \brief Unsigned subtraction: X = |A| - |B|
*
* \param X Destination MPI
* \param A Left-hand MPI
* \param B Right-hand MPI
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_NEGATIVE_VALUE if B is greater than A
*/
int mbedtls_mpi_sub_abs( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B );
/**
* \brief Signed addition: X = A + B
*
* \param X Destination MPI
* \param A Left-hand MPI
* \param B Right-hand MPI
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed
*/
int mbedtls_mpi_add_mpi( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B );
/**
* \brief Signed subtraction: X = A - B
*
* \param X Destination MPI
* \param A Left-hand MPI
* \param B Right-hand MPI
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed
*/
int mbedtls_mpi_sub_mpi( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B );
/**
* \brief Signed addition: X = A + b
*
* \param X Destination MPI
* \param A Left-hand MPI
* \param b The integer value to add
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed
*/
int mbedtls_mpi_add_int( mbedtls_mpi *X, const mbedtls_mpi *A, mbedtls_mpi_sint b );
/**
* \brief Signed subtraction: X = A - b
*
* \param X Destination MPI
* \param A Left-hand MPI
* \param b The integer value to subtract
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed
*/
int mbedtls_mpi_sub_int( mbedtls_mpi *X, const mbedtls_mpi *A, mbedtls_mpi_sint b );
/**
* \brief Baseline multiplication: X = A * B
*
* \param X Destination MPI
* \param A Left-hand MPI
* \param B Right-hand MPI
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed
*/
int mbedtls_mpi_mul_mpi( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B );
/**
* \brief Baseline multiplication: X = A * b
*
* \param X Destination MPI
* \param A Left-hand MPI
* \param b The unsigned integer value to multiply with
*
* \note b is unsigned
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed
*/
int mbedtls_mpi_mul_int( mbedtls_mpi *X, const mbedtls_mpi *A, mbedtls_mpi_uint b );
/**
* \brief Division by mbedtls_mpi: A = Q * B + R
*
* \param Q Destination MPI for the quotient
* \param R Destination MPI for the rest value
* \param A Left-hand MPI
* \param B Right-hand MPI
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed,
* MBEDTLS_ERR_MPI_DIVISION_BY_ZERO if B == 0
*
* \note Either Q or R can be NULL.
*/
int mbedtls_mpi_div_mpi( mbedtls_mpi *Q, mbedtls_mpi *R, const mbedtls_mpi *A, const mbedtls_mpi *B );
/**
* \brief Division by int: A = Q * b + R
*
* \param Q Destination MPI for the quotient
* \param R Destination MPI for the rest value
* \param A Left-hand MPI
* \param b Integer to divide by
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed,
* MBEDTLS_ERR_MPI_DIVISION_BY_ZERO if b == 0
*
* \note Either Q or R can be NULL.
*/
int mbedtls_mpi_div_int( mbedtls_mpi *Q, mbedtls_mpi *R, const mbedtls_mpi *A, mbedtls_mpi_sint b );
/**
* \brief Modulo: R = A mod B
*
* \param R Destination MPI for the rest value
* \param A Left-hand MPI
* \param B Right-hand MPI
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed,
* MBEDTLS_ERR_MPI_DIVISION_BY_ZERO if B == 0,
* MBEDTLS_ERR_MPI_NEGATIVE_VALUE if B < 0
*/
int mbedtls_mpi_mod_mpi( mbedtls_mpi *R, const mbedtls_mpi *A, const mbedtls_mpi *B );
/**
* \brief Modulo: r = A mod b
*
* \param r Destination mbedtls_mpi_uint
* \param A Left-hand MPI
* \param b Integer to divide by
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed,
* MBEDTLS_ERR_MPI_DIVISION_BY_ZERO if b == 0,
* MBEDTLS_ERR_MPI_NEGATIVE_VALUE if b < 0
*/
int mbedtls_mpi_mod_int( mbedtls_mpi_uint *r, const mbedtls_mpi *A, mbedtls_mpi_sint b );
/**
* \brief Sliding-window exponentiation: X = A^E mod N
*
* \param X Destination MPI
* \param A Left-hand MPI
* \param E Exponent MPI
* \param N Modular MPI
* \param _RR Speed-up MPI used for recalculations
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed,
* MBEDTLS_ERR_MPI_BAD_INPUT_DATA if N is negative or even or
* if E is negative
*
* \note _RR is used to avoid re-computing R*R mod N across
* multiple calls, which speeds up things a bit. It can
* be set to NULL if the extra performance is unneeded.
*/
int mbedtls_mpi_exp_mod( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *E, const mbedtls_mpi *N, mbedtls_mpi *_RR );
/**
* \brief Fill an MPI X with size bytes of random
*
* \param X Destination MPI
* \param size Size in bytes
* \param f_rng RNG function
* \param p_rng RNG parameter
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed
*/
int mbedtls_mpi_fill_random( mbedtls_mpi *X, size_t size,
int (*f_rng)(void *, unsigned char *, size_t),
void *p_rng );
/**
* \brief Greatest common divisor: G = gcd(A, B)
*
* \param G Destination MPI
* \param A Left-hand MPI
* \param B Right-hand MPI
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed
*/
int mbedtls_mpi_gcd( mbedtls_mpi *G, const mbedtls_mpi *A, const mbedtls_mpi *B );
/**
* \brief Modular inverse: X = A^-1 mod N
*
* \param X Destination MPI
* \param A Left-hand MPI
* \param N Right-hand MPI
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed,
* MBEDTLS_ERR_MPI_BAD_INPUT_DATA if N is <= 1,
MBEDTLS_ERR_MPI_NOT_ACCEPTABLE if A has no inverse mod N.
*/
int mbedtls_mpi_inv_mod( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *N );
/**
* \brief Miller-Rabin primality test
*
* \param X MPI to check
* \param f_rng RNG function
* \param p_rng RNG parameter
*
* \return 0 if successful (probably prime),
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed,
* MBEDTLS_ERR_MPI_NOT_ACCEPTABLE if X is not prime
*/
int mbedtls_mpi_is_prime( const mbedtls_mpi *X,
int (*f_rng)(void *, unsigned char *, size_t),
void *p_rng );
/**
* \brief Prime number generation
*
* \param X Destination MPI
* \param nbits Required size of X in bits
* ( 3 <= nbits <= MBEDTLS_MPI_MAX_BITS )
* \param dh_flag If 1, then (X-1)/2 will be prime too
* \param f_rng RNG function
* \param p_rng RNG parameter
*
* \return 0 if successful (probably prime),
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed,
* MBEDTLS_ERR_MPI_BAD_INPUT_DATA if nbits is < 3
*/
int mbedtls_mpi_gen_prime( mbedtls_mpi *X, size_t nbits, int dh_flag,
int (*f_rng)(void *, unsigned char *, size_t),
void *p_rng );
/**
* \brief Checkup routine
*
* \return 0 if successful, or 1 if the test failed
*/
int mbedtls_mpi_self_test( int verbose );
#ifdef __cplusplus
}
#endif
#endif /* bignum.h */

View File

@ -0,0 +1,887 @@
/**
* \file bn_mul.h
*
* \brief Multi-precision integer library
*
* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* This file is part of mbed TLS (https://tls.mbed.org)
*/
/*
* Multiply source vector [s] with b, add result
* to destination vector [d] and set carry c.
*
* Currently supports:
*
* . IA-32 (386+) . AMD64 / EM64T
* . IA-32 (SSE2) . Motorola 68000
* . PowerPC, 32-bit . MicroBlaze
* . PowerPC, 64-bit . TriCore
* . SPARC v8 . ARM v3+
* . Alpha . MIPS32
* . C, longlong . C, generic
*/
#ifndef MBEDTLS_BN_MUL_H
#define MBEDTLS_BN_MUL_H
#include "bignum.h"
#if defined(MBEDTLS_HAVE_ASM)
#ifndef asm
#define asm __asm
#endif
/* armcc5 --gnu defines __GNUC__ but doesn't support GNU's extended asm */
#if defined(__GNUC__) && \
( !defined(__ARMCC_VERSION) || __ARMCC_VERSION >= 6000000 )
#if defined(__i386__)
#define MULADDC_INIT \
asm( \
"movl %%ebx, %0 \n\t" \
"movl %5, %%esi \n\t" \
"movl %6, %%edi \n\t" \
"movl %7, %%ecx \n\t" \
"movl %8, %%ebx \n\t"
#define MULADDC_CORE \
"lodsl \n\t" \
"mull %%ebx \n\t" \
"addl %%ecx, %%eax \n\t" \
"adcl $0, %%edx \n\t" \
"addl (%%edi), %%eax \n\t" \
"adcl $0, %%edx \n\t" \
"movl %%edx, %%ecx \n\t" \
"stosl \n\t"
#if defined(MBEDTLS_HAVE_SSE2)
#define MULADDC_HUIT \
"movd %%ecx, %%mm1 \n\t" \
"movd %%ebx, %%mm0 \n\t" \
"movd (%%edi), %%mm3 \n\t" \
"paddq %%mm3, %%mm1 \n\t" \
"movd (%%esi), %%mm2 \n\t" \
"pmuludq %%mm0, %%mm2 \n\t" \
"movd 4(%%esi), %%mm4 \n\t" \
"pmuludq %%mm0, %%mm4 \n\t" \
"movd 8(%%esi), %%mm6 \n\t" \
"pmuludq %%mm0, %%mm6 \n\t" \
"movd 12(%%esi), %%mm7 \n\t" \
"pmuludq %%mm0, %%mm7 \n\t" \
"paddq %%mm2, %%mm1 \n\t" \
"movd 4(%%edi), %%mm3 \n\t" \
"paddq %%mm4, %%mm3 \n\t" \
"movd 8(%%edi), %%mm5 \n\t" \
"paddq %%mm6, %%mm5 \n\t" \
"movd 12(%%edi), %%mm4 \n\t" \
"paddq %%mm4, %%mm7 \n\t" \
"movd %%mm1, (%%edi) \n\t" \
"movd 16(%%esi), %%mm2 \n\t" \
"pmuludq %%mm0, %%mm2 \n\t" \
"psrlq $32, %%mm1 \n\t" \
"movd 20(%%esi), %%mm4 \n\t" \
"pmuludq %%mm0, %%mm4 \n\t" \
"paddq %%mm3, %%mm1 \n\t" \
"movd 24(%%esi), %%mm6 \n\t" \
"pmuludq %%mm0, %%mm6 \n\t" \
"movd %%mm1, 4(%%edi) \n\t" \
"psrlq $32, %%mm1 \n\t" \
"movd 28(%%esi), %%mm3 \n\t" \
"pmuludq %%mm0, %%mm3 \n\t" \
"paddq %%mm5, %%mm1 \n\t" \
"movd 16(%%edi), %%mm5 \n\t" \
"paddq %%mm5, %%mm2 \n\t" \
"movd %%mm1, 8(%%edi) \n\t" \
"psrlq $32, %%mm1 \n\t" \
"paddq %%mm7, %%mm1 \n\t" \
"movd 20(%%edi), %%mm5 \n\t" \
"paddq %%mm5, %%mm4 \n\t" \
"movd %%mm1, 12(%%edi) \n\t" \
"psrlq $32, %%mm1 \n\t" \
"paddq %%mm2, %%mm1 \n\t" \
"movd 24(%%edi), %%mm5 \n\t" \
"paddq %%mm5, %%mm6 \n\t" \
"movd %%mm1, 16(%%edi) \n\t" \
"psrlq $32, %%mm1 \n\t" \
"paddq %%mm4, %%mm1 \n\t" \
"movd 28(%%edi), %%mm5 \n\t" \
"paddq %%mm5, %%mm3 \n\t" \
"movd %%mm1, 20(%%edi) \n\t" \
"psrlq $32, %%mm1 \n\t" \
"paddq %%mm6, %%mm1 \n\t" \
"movd %%mm1, 24(%%edi) \n\t" \
"psrlq $32, %%mm1 \n\t" \
"paddq %%mm3, %%mm1 \n\t" \
"movd %%mm1, 28(%%edi) \n\t" \
"addl $32, %%edi \n\t" \
"addl $32, %%esi \n\t" \
"psrlq $32, %%mm1 \n\t" \
"movd %%mm1, %%ecx \n\t"
#define MULADDC_STOP \
"emms \n\t" \
"movl %4, %%ebx \n\t" \
"movl %%ecx, %1 \n\t" \
"movl %%edi, %2 \n\t" \
"movl %%esi, %3 \n\t" \
: "=m" (t), "=m" (c), "=m" (d), "=m" (s) \
: "m" (t), "m" (s), "m" (d), "m" (c), "m" (b) \
: "eax", "ecx", "edx", "esi", "edi" \
);
#else
#define MULADDC_STOP \
"movl %4, %%ebx \n\t" \
"movl %%ecx, %1 \n\t" \
"movl %%edi, %2 \n\t" \
"movl %%esi, %3 \n\t" \
: "=m" (t), "=m" (c), "=m" (d), "=m" (s) \
: "m" (t), "m" (s), "m" (d), "m" (c), "m" (b) \
: "eax", "ecx", "edx", "esi", "edi" \
);
#endif /* SSE2 */
#endif /* i386 */
#if defined(__amd64__) || defined (__x86_64__)
#define MULADDC_INIT \
asm( \
"xorq %%r8, %%r8 \n\t"
#define MULADDC_CORE \
"movq (%%rsi), %%rax \n\t" \
"mulq %%rbx \n\t" \
"addq $8, %%rsi \n\t" \
"addq %%rcx, %%rax \n\t" \
"movq %%r8, %%rcx \n\t" \
"adcq $0, %%rdx \n\t" \
"nop \n\t" \
"addq %%rax, (%%rdi) \n\t" \
"adcq %%rdx, %%rcx \n\t" \
"addq $8, %%rdi \n\t"
#define MULADDC_STOP \
: "+c" (c), "+D" (d), "+S" (s) \
: "b" (b) \
: "rax", "rdx", "r8" \
);
#endif /* AMD64 */
#if defined(__mc68020__) || defined(__mcpu32__)
#define MULADDC_INIT \
asm( \
"movl %3, %%a2 \n\t" \
"movl %4, %%a3 \n\t" \
"movl %5, %%d3 \n\t" \
"movl %6, %%d2 \n\t" \
"moveq #0, %%d0 \n\t"
#define MULADDC_CORE \
"movel %%a2@+, %%d1 \n\t" \
"mulul %%d2, %%d4:%%d1 \n\t" \
"addl %%d3, %%d1 \n\t" \
"addxl %%d0, %%d4 \n\t" \
"moveq #0, %%d3 \n\t" \
"addl %%d1, %%a3@+ \n\t" \
"addxl %%d4, %%d3 \n\t"
#define MULADDC_STOP \
"movl %%d3, %0 \n\t" \
"movl %%a3, %1 \n\t" \
"movl %%a2, %2 \n\t" \
: "=m" (c), "=m" (d), "=m" (s) \
: "m" (s), "m" (d), "m" (c), "m" (b) \
: "d0", "d1", "d2", "d3", "d4", "a2", "a3" \
);
#define MULADDC_HUIT \
"movel %%a2@+, %%d1 \n\t" \
"mulul %%d2, %%d4:%%d1 \n\t" \
"addxl %%d3, %%d1 \n\t" \
"addxl %%d0, %%d4 \n\t" \
"addl %%d1, %%a3@+ \n\t" \
"movel %%a2@+, %%d1 \n\t" \
"mulul %%d2, %%d3:%%d1 \n\t" \
"addxl %%d4, %%d1 \n\t" \
"addxl %%d0, %%d3 \n\t" \
"addl %%d1, %%a3@+ \n\t" \
"movel %%a2@+, %%d1 \n\t" \
"mulul %%d2, %%d4:%%d1 \n\t" \
"addxl %%d3, %%d1 \n\t" \
"addxl %%d0, %%d4 \n\t" \
"addl %%d1, %%a3@+ \n\t" \
"movel %%a2@+, %%d1 \n\t" \
"mulul %%d2, %%d3:%%d1 \n\t" \
"addxl %%d4, %%d1 \n\t" \
"addxl %%d0, %%d3 \n\t" \
"addl %%d1, %%a3@+ \n\t" \
"movel %%a2@+, %%d1 \n\t" \
"mulul %%d2, %%d4:%%d1 \n\t" \
"addxl %%d3, %%d1 \n\t" \
"addxl %%d0, %%d4 \n\t" \
"addl %%d1, %%a3@+ \n\t" \
"movel %%a2@+, %%d1 \n\t" \
"mulul %%d2, %%d3:%%d1 \n\t" \
"addxl %%d4, %%d1 \n\t" \
"addxl %%d0, %%d3 \n\t" \
"addl %%d1, %%a3@+ \n\t" \
"movel %%a2@+, %%d1 \n\t" \
"mulul %%d2, %%d4:%%d1 \n\t" \
"addxl %%d3, %%d1 \n\t" \
"addxl %%d0, %%d4 \n\t" \
"addl %%d1, %%a3@+ \n\t" \
"movel %%a2@+, %%d1 \n\t" \
"mulul %%d2, %%d3:%%d1 \n\t" \
"addxl %%d4, %%d1 \n\t" \
"addxl %%d0, %%d3 \n\t" \
"addl %%d1, %%a3@+ \n\t" \
"addxl %%d0, %%d3 \n\t"
#endif /* MC68000 */
#if defined(__powerpc64__) || defined(__ppc64__)
#if defined(__MACH__) && defined(__APPLE__)
#define MULADDC_INIT \
asm( \
"ld r3, %3 \n\t" \
"ld r4, %4 \n\t" \
"ld r5, %5 \n\t" \
"ld r6, %6 \n\t" \
"addi r3, r3, -8 \n\t" \
"addi r4, r4, -8 \n\t" \
"addic r5, r5, 0 \n\t"
#define MULADDC_CORE \
"ldu r7, 8(r3) \n\t" \
"mulld r8, r7, r6 \n\t" \
"mulhdu r9, r7, r6 \n\t" \
"adde r8, r8, r5 \n\t" \
"ld r7, 8(r4) \n\t" \
"addze r5, r9 \n\t" \
"addc r8, r8, r7 \n\t" \
"stdu r8, 8(r4) \n\t"
#define MULADDC_STOP \
"addze r5, r5 \n\t" \
"addi r4, r4, 8 \n\t" \
"addi r3, r3, 8 \n\t" \
"std r5, %0 \n\t" \
"std r4, %1 \n\t" \
"std r3, %2 \n\t" \
: "=m" (c), "=m" (d), "=m" (s) \
: "m" (s), "m" (d), "m" (c), "m" (b) \
: "r3", "r4", "r5", "r6", "r7", "r8", "r9" \
);
#else /* __MACH__ && __APPLE__ */
#define MULADDC_INIT \
asm( \
"ld %%r3, %3 \n\t" \
"ld %%r4, %4 \n\t" \
"ld %%r5, %5 \n\t" \
"ld %%r6, %6 \n\t" \
"addi %%r3, %%r3, -8 \n\t" \
"addi %%r4, %%r4, -8 \n\t" \
"addic %%r5, %%r5, 0 \n\t"
#define MULADDC_CORE \
"ldu %%r7, 8(%%r3) \n\t" \
"mulld %%r8, %%r7, %%r6 \n\t" \
"mulhdu %%r9, %%r7, %%r6 \n\t" \
"adde %%r8, %%r8, %%r5 \n\t" \
"ld %%r7, 8(%%r4) \n\t" \
"addze %%r5, %%r9 \n\t" \
"addc %%r8, %%r8, %%r7 \n\t" \
"stdu %%r8, 8(%%r4) \n\t"
#define MULADDC_STOP \
"addze %%r5, %%r5 \n\t" \
"addi %%r4, %%r4, 8 \n\t" \
"addi %%r3, %%r3, 8 \n\t" \
"std %%r5, %0 \n\t" \
"std %%r4, %1 \n\t" \
"std %%r3, %2 \n\t" \
: "=m" (c), "=m" (d), "=m" (s) \
: "m" (s), "m" (d), "m" (c), "m" (b) \
: "r3", "r4", "r5", "r6", "r7", "r8", "r9" \
);
#endif /* __MACH__ && __APPLE__ */
#elif defined(__powerpc__) || defined(__ppc__) /* end PPC64/begin PPC32 */
#if defined(__MACH__) && defined(__APPLE__)
#define MULADDC_INIT \
asm( \
"lwz r3, %3 \n\t" \
"lwz r4, %4 \n\t" \
"lwz r5, %5 \n\t" \
"lwz r6, %6 \n\t" \
"addi r3, r3, -4 \n\t" \
"addi r4, r4, -4 \n\t" \
"addic r5, r5, 0 \n\t"
#define MULADDC_CORE \
"lwzu r7, 4(r3) \n\t" \
"mullw r8, r7, r6 \n\t" \
"mulhwu r9, r7, r6 \n\t" \
"adde r8, r8, r5 \n\t" \
"lwz r7, 4(r4) \n\t" \
"addze r5, r9 \n\t" \
"addc r8, r8, r7 \n\t" \
"stwu r8, 4(r4) \n\t"
#define MULADDC_STOP \
"addze r5, r5 \n\t" \
"addi r4, r4, 4 \n\t" \
"addi r3, r3, 4 \n\t" \
"stw r5, %0 \n\t" \
"stw r4, %1 \n\t" \
"stw r3, %2 \n\t" \
: "=m" (c), "=m" (d), "=m" (s) \
: "m" (s), "m" (d), "m" (c), "m" (b) \
: "r3", "r4", "r5", "r6", "r7", "r8", "r9" \
);
#else /* __MACH__ && __APPLE__ */
#define MULADDC_INIT \
asm( \
"lwz %%r3, %3 \n\t" \
"lwz %%r4, %4 \n\t" \
"lwz %%r5, %5 \n\t" \
"lwz %%r6, %6 \n\t" \
"addi %%r3, %%r3, -4 \n\t" \
"addi %%r4, %%r4, -4 \n\t" \
"addic %%r5, %%r5, 0 \n\t"
#define MULADDC_CORE \
"lwzu %%r7, 4(%%r3) \n\t" \
"mullw %%r8, %%r7, %%r6 \n\t" \
"mulhwu %%r9, %%r7, %%r6 \n\t" \
"adde %%r8, %%r8, %%r5 \n\t" \
"lwz %%r7, 4(%%r4) \n\t" \
"addze %%r5, %%r9 \n\t" \
"addc %%r8, %%r8, %%r7 \n\t" \
"stwu %%r8, 4(%%r4) \n\t"
#define MULADDC_STOP \
"addze %%r5, %%r5 \n\t" \
"addi %%r4, %%r4, 4 \n\t" \
"addi %%r3, %%r3, 4 \n\t" \
"stw %%r5, %0 \n\t" \
"stw %%r4, %1 \n\t" \
"stw %%r3, %2 \n\t" \
: "=m" (c), "=m" (d), "=m" (s) \
: "m" (s), "m" (d), "m" (c), "m" (b) \
: "r3", "r4", "r5", "r6", "r7", "r8", "r9" \
);
#endif /* __MACH__ && __APPLE__ */
#endif /* PPC32 */
/*
* The Sparc(64) assembly is reported to be broken.
* Disable it for now, until we're able to fix it.
*/
#if 0 && defined(__sparc__)
#if defined(__sparc64__)
#define MULADDC_INIT \
asm( \
"ldx %3, %%o0 \n\t" \
"ldx %4, %%o1 \n\t" \
"ld %5, %%o2 \n\t" \
"ld %6, %%o3 \n\t"
#define MULADDC_CORE \
"ld [%%o0], %%o4 \n\t" \
"inc 4, %%o0 \n\t" \
"ld [%%o1], %%o5 \n\t" \
"umul %%o3, %%o4, %%o4 \n\t" \
"addcc %%o4, %%o2, %%o4 \n\t" \
"rd %%y, %%g1 \n\t" \
"addx %%g1, 0, %%g1 \n\t" \
"addcc %%o4, %%o5, %%o4 \n\t" \
"st %%o4, [%%o1] \n\t" \
"addx %%g1, 0, %%o2 \n\t" \
"inc 4, %%o1 \n\t"
#define MULADDC_STOP \
"st %%o2, %0 \n\t" \
"stx %%o1, %1 \n\t" \
"stx %%o0, %2 \n\t" \
: "=m" (c), "=m" (d), "=m" (s) \
: "m" (s), "m" (d), "m" (c), "m" (b) \
: "g1", "o0", "o1", "o2", "o3", "o4", \
"o5" \
);
#else /* __sparc64__ */
#define MULADDC_INIT \
asm( \
"ld %3, %%o0 \n\t" \
"ld %4, %%o1 \n\t" \
"ld %5, %%o2 \n\t" \
"ld %6, %%o3 \n\t"
#define MULADDC_CORE \
"ld [%%o0], %%o4 \n\t" \
"inc 4, %%o0 \n\t" \
"ld [%%o1], %%o5 \n\t" \
"umul %%o3, %%o4, %%o4 \n\t" \
"addcc %%o4, %%o2, %%o4 \n\t" \
"rd %%y, %%g1 \n\t" \
"addx %%g1, 0, %%g1 \n\t" \
"addcc %%o4, %%o5, %%o4 \n\t" \
"st %%o4, [%%o1] \n\t" \
"addx %%g1, 0, %%o2 \n\t" \
"inc 4, %%o1 \n\t"
#define MULADDC_STOP \
"st %%o2, %0 \n\t" \
"st %%o1, %1 \n\t" \
"st %%o0, %2 \n\t" \
: "=m" (c), "=m" (d), "=m" (s) \
: "m" (s), "m" (d), "m" (c), "m" (b) \
: "g1", "o0", "o1", "o2", "o3", "o4", \
"o5" \
);
#endif /* __sparc64__ */
#endif /* __sparc__ */
#if defined(__microblaze__) || defined(microblaze)
#define MULADDC_INIT \
asm( \
"lwi r3, %3 \n\t" \
"lwi r4, %4 \n\t" \
"lwi r5, %5 \n\t" \
"lwi r6, %6 \n\t" \
"andi r7, r6, 0xffff \n\t" \
"bsrli r6, r6, 16 \n\t"
#define MULADDC_CORE \
"lhui r8, r3, 0 \n\t" \
"addi r3, r3, 2 \n\t" \
"lhui r9, r3, 0 \n\t" \
"addi r3, r3, 2 \n\t" \
"mul r10, r9, r6 \n\t" \
"mul r11, r8, r7 \n\t" \
"mul r12, r9, r7 \n\t" \
"mul r13, r8, r6 \n\t" \
"bsrli r8, r10, 16 \n\t" \
"bsrli r9, r11, 16 \n\t" \
"add r13, r13, r8 \n\t" \
"add r13, r13, r9 \n\t" \
"bslli r10, r10, 16 \n\t" \
"bslli r11, r11, 16 \n\t" \
"add r12, r12, r10 \n\t" \
"addc r13, r13, r0 \n\t" \
"add r12, r12, r11 \n\t" \
"addc r13, r13, r0 \n\t" \
"lwi r10, r4, 0 \n\t" \
"add r12, r12, r10 \n\t" \
"addc r13, r13, r0 \n\t" \
"add r12, r12, r5 \n\t" \
"addc r5, r13, r0 \n\t" \
"swi r12, r4, 0 \n\t" \
"addi r4, r4, 4 \n\t"
#define MULADDC_STOP \
"swi r5, %0 \n\t" \
"swi r4, %1 \n\t" \
"swi r3, %2 \n\t" \
: "=m" (c), "=m" (d), "=m" (s) \
: "m" (s), "m" (d), "m" (c), "m" (b) \
: "r3", "r4" "r5", "r6", "r7", "r8", \
"r9", "r10", "r11", "r12", "r13" \
);
#endif /* MicroBlaze */
#if defined(__tricore__)
#define MULADDC_INIT \
asm( \
"ld.a %%a2, %3 \n\t" \
"ld.a %%a3, %4 \n\t" \
"ld.w %%d4, %5 \n\t" \
"ld.w %%d1, %6 \n\t" \
"xor %%d5, %%d5 \n\t"
#define MULADDC_CORE \
"ld.w %%d0, [%%a2+] \n\t" \
"madd.u %%e2, %%e4, %%d0, %%d1 \n\t" \
"ld.w %%d0, [%%a3] \n\t" \
"addx %%d2, %%d2, %%d0 \n\t" \
"addc %%d3, %%d3, 0 \n\t" \
"mov %%d4, %%d3 \n\t" \
"st.w [%%a3+], %%d2 \n\t"
#define MULADDC_STOP \
"st.w %0, %%d4 \n\t" \
"st.a %1, %%a3 \n\t" \
"st.a %2, %%a2 \n\t" \
: "=m" (c), "=m" (d), "=m" (s) \
: "m" (s), "m" (d), "m" (c), "m" (b) \
: "d0", "d1", "e2", "d4", "a2", "a3" \
);
#endif /* TriCore */
/*
* gcc -O0 by default uses r7 for the frame pointer, so it complains about our
* use of r7 below, unless -fomit-frame-pointer is passed. Unfortunately,
* passing that option is not easy when building with yotta.
*
* On the other hand, -fomit-frame-pointer is implied by any -Ox options with
* x !=0, which we can detect using __OPTIMIZE__ (which is also defined by
* clang and armcc5 under the same conditions).
*
* So, only use the optimized assembly below for optimized build, which avoids
* the build error and is pretty reasonable anyway.
*/
#if defined(__GNUC__) && !defined(__OPTIMIZE__)
#define MULADDC_CANNOT_USE_R7
#endif
#if defined(__arm__) && !defined(MULADDC_CANNOT_USE_R7)
#if defined(__thumb__) && !defined(__thumb2__)
#pragma message "using ARM THUMB MULADDC"
#define MULADDC_INIT \
asm( \
"ldr r0, %3 \n\t" \
"ldr r1, %4 \n\t" \
"ldr r2, %5 \n\t" \
"ldr r3, %6 \n\t" \
"lsr r7, r3, #16 \n\t" \
"mov r9, r7 \n\t" \
"lsl r7, r3, #16 \n\t" \
"lsr r7, r7, #16 \n\t" \
"mov r8, r7 \n\t"
#define MULADDC_CORE \
"ldmia r0!, {r6} \n\t" \
"lsr r7, r6, #16 \n\t" \
"lsl r6, r6, #16 \n\t" \
"lsr r6, r6, #16 \n\t" \
"mov r4, r8 \n\t" \
"mul r4, r6 \n\t" \
"mov r3, r9 \n\t" \
"mul r6, r3 \n\t" \
"mov r5, r9 \n\t" \
"mul r5, r7 \n\t" \
"mov r3, r8 \n\t" \
"mul r7, r3 \n\t" \
"lsr r3, r6, #16 \n\t" \
"add r5, r5, r3 \n\t" \
"lsr r3, r7, #16 \n\t" \
"add r5, r5, r3 \n\t" \
"add r4, r4, r2 \n\t" \
"mov r2, #0 \n\t" \
"adc r5, r2 \n\t" \
"lsl r3, r6, #16 \n\t" \
"add r4, r4, r3 \n\t" \
"adc r5, r2 \n\t" \
"lsl r3, r7, #16 \n\t" \
"add r4, r4, r3 \n\t" \
"adc r5, r2 \n\t" \
"ldr r3, [r1] \n\t" \
"add r4, r4, r3 \n\t" \
"adc r2, r5 \n\t" \
"stmia r1!, {r4} \n\t"
#define MULADDC_STOP \
"str r2, %0 \n\t" \
"str r1, %1 \n\t" \
"str r0, %2 \n\t" \
: "=m" (c), "=m" (d), "=m" (s) \
: "m" (s), "m" (d), "m" (c), "m" (b) \
: "r0", "r1", "r2", "r3", "r4", "r5", \
"r6", "r7", "r8", "r9", "cc" \
);
#else
#define MULADDC_INIT \
asm( \
"ldr r0, %3 \n\t" \
"ldr r1, %4 \n\t" \
"ldr r2, %5 \n\t" \
"ldr r3, %6 \n\t"
#define MULADDC_CORE \
"ldr r4, [r0], #4 \n\t" \
"mov r5, #0 \n\t" \
"ldr r6, [r1] \n\t" \
"umlal r2, r5, r3, r4 \n\t" \
"adds r7, r6, r2 \n\t" \
"adc r2, r5, #0 \n\t" \
"str r7, [r1], #4 \n\t"
#define MULADDC_STOP \
"str r2, %0 \n\t" \
"str r1, %1 \n\t" \
"str r0, %2 \n\t" \
: "=m" (c), "=m" (d), "=m" (s) \
: "m" (s), "m" (d), "m" (c), "m" (b) \
: "r0", "r1", "r2", "r3", "r4", "r5", \
"r6", "r7", "cc" \
);
#endif /* Thumb */
#endif /* ARMv3 */
#if defined(__alpha__)
#define MULADDC_INIT \
asm( \
"ldq $1, %3 \n\t" \
"ldq $2, %4 \n\t" \
"ldq $3, %5 \n\t" \
"ldq $4, %6 \n\t"
#define MULADDC_CORE \
"ldq $6, 0($1) \n\t" \
"addq $1, 8, $1 \n\t" \
"mulq $6, $4, $7 \n\t" \
"umulh $6, $4, $6 \n\t" \
"addq $7, $3, $7 \n\t" \
"cmpult $7, $3, $3 \n\t" \
"ldq $5, 0($2) \n\t" \
"addq $7, $5, $7 \n\t" \
"cmpult $7, $5, $5 \n\t" \
"stq $7, 0($2) \n\t" \
"addq $2, 8, $2 \n\t" \
"addq $6, $3, $3 \n\t" \
"addq $5, $3, $3 \n\t"
#define MULADDC_STOP \
"stq $3, %0 \n\t" \
"stq $2, %1 \n\t" \
"stq $1, %2 \n\t" \
: "=m" (c), "=m" (d), "=m" (s) \
: "m" (s), "m" (d), "m" (c), "m" (b) \
: "$1", "$2", "$3", "$4", "$5", "$6", "$7" \
);
#endif /* Alpha */
#if defined(__mips__) && !defined(__mips64)
#define MULADDC_INIT \
asm( \
"lw $10, %3 \n\t" \
"lw $11, %4 \n\t" \
"lw $12, %5 \n\t" \
"lw $13, %6 \n\t"
#define MULADDC_CORE \
"lw $14, 0($10) \n\t" \
"multu $13, $14 \n\t" \
"addi $10, $10, 4 \n\t" \
"mflo $14 \n\t" \
"mfhi $9 \n\t" \
"addu $14, $12, $14 \n\t" \
"lw $15, 0($11) \n\t" \
"sltu $12, $14, $12 \n\t" \
"addu $15, $14, $15 \n\t" \
"sltu $14, $15, $14 \n\t" \
"addu $12, $12, $9 \n\t" \
"sw $15, 0($11) \n\t" \
"addu $12, $12, $14 \n\t" \
"addi $11, $11, 4 \n\t"
#define MULADDC_STOP \
"sw $12, %0 \n\t" \
"sw $11, %1 \n\t" \
"sw $10, %2 \n\t" \
: "=m" (c), "=m" (d), "=m" (s) \
: "m" (s), "m" (d), "m" (c), "m" (b) \
: "$9", "$10", "$11", "$12", "$13", "$14", "$15" \
);
#endif /* MIPS */
#endif /* GNUC */
#if (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
#define MULADDC_INIT \
__asm mov esi, s \
__asm mov edi, d \
__asm mov ecx, c \
__asm mov ebx, b
#define MULADDC_CORE \
__asm lodsd \
__asm mul ebx \
__asm add eax, ecx \
__asm adc edx, 0 \
__asm add eax, [edi] \
__asm adc edx, 0 \
__asm mov ecx, edx \
__asm stosd
#if defined(MBEDTLS_HAVE_SSE2)
#define EMIT __asm _emit
#define MULADDC_HUIT \
EMIT 0x0F EMIT 0x6E EMIT 0xC9 \
EMIT 0x0F EMIT 0x6E EMIT 0xC3 \
EMIT 0x0F EMIT 0x6E EMIT 0x1F \
EMIT 0x0F EMIT 0xD4 EMIT 0xCB \
EMIT 0x0F EMIT 0x6E EMIT 0x16 \
EMIT 0x0F EMIT 0xF4 EMIT 0xD0 \
EMIT 0x0F EMIT 0x6E EMIT 0x66 EMIT 0x04 \
EMIT 0x0F EMIT 0xF4 EMIT 0xE0 \
EMIT 0x0F EMIT 0x6E EMIT 0x76 EMIT 0x08 \
EMIT 0x0F EMIT 0xF4 EMIT 0xF0 \
EMIT 0x0F EMIT 0x6E EMIT 0x7E EMIT 0x0C \
EMIT 0x0F EMIT 0xF4 EMIT 0xF8 \
EMIT 0x0F EMIT 0xD4 EMIT 0xCA \
EMIT 0x0F EMIT 0x6E EMIT 0x5F EMIT 0x04 \
EMIT 0x0F EMIT 0xD4 EMIT 0xDC \
EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x08 \
EMIT 0x0F EMIT 0xD4 EMIT 0xEE \
EMIT 0x0F EMIT 0x6E EMIT 0x67 EMIT 0x0C \
EMIT 0x0F EMIT 0xD4 EMIT 0xFC \
EMIT 0x0F EMIT 0x7E EMIT 0x0F \
EMIT 0x0F EMIT 0x6E EMIT 0x56 EMIT 0x10 \
EMIT 0x0F EMIT 0xF4 EMIT 0xD0 \
EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \
EMIT 0x0F EMIT 0x6E EMIT 0x66 EMIT 0x14 \
EMIT 0x0F EMIT 0xF4 EMIT 0xE0 \
EMIT 0x0F EMIT 0xD4 EMIT 0xCB \
EMIT 0x0F EMIT 0x6E EMIT 0x76 EMIT 0x18 \
EMIT 0x0F EMIT 0xF4 EMIT 0xF0 \
EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x04 \
EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \
EMIT 0x0F EMIT 0x6E EMIT 0x5E EMIT 0x1C \
EMIT 0x0F EMIT 0xF4 EMIT 0xD8 \
EMIT 0x0F EMIT 0xD4 EMIT 0xCD \
EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x10 \
EMIT 0x0F EMIT 0xD4 EMIT 0xD5 \
EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x08 \
EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \
EMIT 0x0F EMIT 0xD4 EMIT 0xCF \
EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x14 \
EMIT 0x0F EMIT 0xD4 EMIT 0xE5 \
EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x0C \
EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \
EMIT 0x0F EMIT 0xD4 EMIT 0xCA \
EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x18 \
EMIT 0x0F EMIT 0xD4 EMIT 0xF5 \
EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x10 \
EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \
EMIT 0x0F EMIT 0xD4 EMIT 0xCC \
EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x1C \
EMIT 0x0F EMIT 0xD4 EMIT 0xDD \
EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x14 \
EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \
EMIT 0x0F EMIT 0xD4 EMIT 0xCE \
EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x18 \
EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \
EMIT 0x0F EMIT 0xD4 EMIT 0xCB \
EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x1C \
EMIT 0x83 EMIT 0xC7 EMIT 0x20 \
EMIT 0x83 EMIT 0xC6 EMIT 0x20 \
EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \
EMIT 0x0F EMIT 0x7E EMIT 0xC9
#define MULADDC_STOP \
EMIT 0x0F EMIT 0x77 \
__asm mov c, ecx \
__asm mov d, edi \
__asm mov s, esi \
#else
#define MULADDC_STOP \
__asm mov c, ecx \
__asm mov d, edi \
__asm mov s, esi \
#endif /* SSE2 */
#endif /* MSVC */
#endif /* MBEDTLS_HAVE_ASM */
#if !defined(MULADDC_CORE)
#if defined(MBEDTLS_HAVE_UDBL)
#define MULADDC_INIT \
{ \
mbedtls_t_udbl r; \
mbedtls_mpi_uint r0, r1;
#define MULADDC_CORE \
r = *(s++) * (mbedtls_t_udbl) b; \
r0 = (mbedtls_mpi_uint) r; \
r1 = (mbedtls_mpi_uint)( r >> biL ); \
r0 += c; r1 += (r0 < c); \
r0 += *d; r1 += (r0 < *d); \
c = r1; *(d++) = r0;
#define MULADDC_STOP \
}
#else
#define MULADDC_INIT \
{ \
mbedtls_mpi_uint s0, s1, b0, b1; \
mbedtls_mpi_uint r0, r1, rx, ry; \
b0 = ( b << biH ) >> biH; \
b1 = ( b >> biH );
#define MULADDC_CORE \
s0 = ( *s << biH ) >> biH; \
s1 = ( *s >> biH ); s++; \
rx = s0 * b1; r0 = s0 * b0; \
ry = s1 * b0; r1 = s1 * b1; \
r1 += ( rx >> biH ); \
r1 += ( ry >> biH ); \
rx <<= biH; ry <<= biH; \
r0 += rx; r1 += (r0 < rx); \
r0 += ry; r1 += (r0 < ry); \
r0 += c; r1 += (r0 < c); \
r0 += *d; r1 += (r0 < *d); \
c = r1; *(d++) = r0;
#define MULADDC_STOP \
}
#endif /* C (generic) */
#endif /* C (longlong) */
#endif /* bn_mul.h */

View File

@ -0,0 +1,5 @@
#define MBEDTLS_BIGNUM_C
#define POLARSSL_AES_C
#define MBEDTLS_HAVE_ASM

View File

@ -0,0 +1,98 @@
/**
* \file padlock.h
*
* Copyright (C) 2006-2010, Brainspark B.V.
*
* This file is part of PolarSSL (http://www.polarssl.org)
* Lead Maintainer: Paul Bakker <polarssl_maintainer at polarssl.org>
*
* All rights reserved.
*
* 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.
*
* 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, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef POLARSSL_PADLOCK_H
#define POLARSSL_PADLOCK_H
#include "aes.h"
#if defined(POLARSSL_HAVE_ASM) && defined(__GNUC__) && defined(__i386__)
#ifndef POLARSSL_HAVE_X86
#define POLARSSL_HAVE_X86
#endif
#define PADLOCK_RNG 0x000C
#define PADLOCK_ACE 0x00C0
#define PADLOCK_PHE 0x0C00
#define PADLOCK_PMM 0x3000
#define PADLOCK_ALIGN16(x) (unsigned long *) (16 + ((long) x & ~15))
#define POLARSSL_ERR_PADLOCK_DATA_MISALIGNED -0x08E0
#ifdef __cplusplus
extern "C" {
#endif
/**
* \brief PadLock detection routine
*
* \param The feature to detect
*
* \return 1 if CPU has support for the feature, 0 otherwise
*/
int padlock_supports( int feature );
/**
* \brief PadLock AES-ECB block en(de)cryption
*
* \param ctx AES context
* \param mode AES_ENCRYPT or AES_DECRYPT
* \param input 16-byte input block
* \param output 16-byte output block
*
* \return 0 if success, 1 if operation failed
*/
int padlock_xcryptecb( aes_context *ctx,
int mode,
const unsigned char input[16],
unsigned char output[16] );
/**
* \brief PadLock AES-CBC buffer en(de)cryption
*
* \param ctx AES context
* \param mode AES_ENCRYPT or AES_DECRYPT
* \param length length of the input data
* \param iv initialization vector (updated after use)
* \param input buffer holding the input data
* \param output buffer holding the output data
*
* \return 0 if success, 1 if operation failed
*/
int padlock_xcryptcbc( aes_context *ctx,
int mode,
int length,
unsigned char iv[16],
const unsigned char *input,
unsigned char *output );
#ifdef __cplusplus
}
#endif
#endif /* HAVE_X86 */
#endif /* padlock.h */

107
arm9/src/nand/sector0.c Normal file
View File

@ -0,0 +1,107 @@
/* sector0.c
* This code was imported from https://github.com/DS-Homebrew/GodMode9i
*
* Changes against the source:
* - Documentation added
* - clean up formatting
* - moved magic numbers to defines from sector0.h
* - fixed parse_mbr to return valid even when signature was invalid but
* the bootstrap region was not all zero. (? Why was this code there ?)
* - removed verbose output (must be handled on application level, not
* within the helpers
*/
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include "sector0.h"
/************************ Constants / Defines *********************************/
static const mbr_partition_t ptable_DSi[MBR_PARTITIONS] = {
{0u, {3u, 24u, 4u}, 6u, {15u, 224u, 59u}, 0x00000877u, 0x00066f89u},
{0u, {2u, 206u, 60u}, 6u, {15u, 224u, 190u}, 0x0006784u, 0x000105b3u},
{0u, {2u, 222u, 191u}, 1u, {15u, 224u, 191u}, 0x00077e5u, 0x000001a3u},
{0u, {0u, 0u, 0u}, 0u, {0u, 0u, 0u}, 0u, 0u}
};
static const mbr_partition_t ptable_3DS[MBR_PARTITIONS] = {
{0u, {4u, 24u, 0u}, 6u, {1u, 160u, 63u}, 0x0000009u, 0x00047da9u},
{0u, {4u, 142u, 64u}, 6u, {1u, 160u, 195u}, 0x0004808u, 0x000105b3u},
{0u, {0u, 0u, 0u}, 0u, {0u, 0u, 0u}, 0u, 0u},
{0u, {0u, 0u, 0u}, 0u, {0u, 0u, 0u}, 0u, 0u}
};
/************************ Functions *******************************************/
/*! \brief Sanity check of the NCSD
*
* The ncsd is checked for
* - the signature magic
* - the partition types
* to ensure a valid 3DS ncsd is present
*
* Return values:
* 0: NCSD is a valid
* -1: the signature/magic is invalid
* -2: at least one unknown partition type was found
*/
int parse_ncsd(const uint8_t sector0[SECTOR_SIZE])
{
const ncsd_header_t * h = (ncsd_header_t *)sector0;
if (NCSD_MAGIC != h->magic)
{
return -1;
}
for (unsigned i = 0; i < NCSD_PARTITIONS; ++i)
{
unsigned fs_type = h->fs_types[i];
if (fs_type == 0)
{
break;
}
switch (fs_type)
{
case 1:
case 3:
case 4:
break;
default:
return -2;
}
}
return 0;
}
/*! \brief Sanity check of the MBR
*
* The master boot record is checked for
* - the signature
* - the partition0 values
* to ensure a valid DSi main partition can be found
*
* Return values:
* 0: MBR is a valid DSi partition
* -1: the signature is invalid
* -2: the first partition does not match expected values
*/
int parse_mbr(const uint8_t sector0[SECTOR_SIZE], const int is3DS)
{
const mbr_t *m = (mbr_t*)sector0;
const mbr_partition_t *ref_ptable; // reference partition table
if ((MBR_SIGNATURE_0 != m->boot_signature[0]) || (MBR_SIGNATURE_1 != m->boot_signature[1]))
{
// if the signature is invalid, the bootsector shall not be used!
return -1;
}
ref_ptable = is3DS?ptable_3DS:ptable_DSi;
// only test the 1st partition now, we've seen variations on the 3rd partition
// and after all we only care about the 1st partition
if (memcmp(ref_ptable, m->partitions, sizeof(mbr_partition_t)))
{
return -2;
}
return 0;
}

164
arm9/src/nand/sector0.h Normal file
View File

@ -0,0 +1,164 @@
/* sector0.h
* This code was imported from https://github.com/DS-Homebrew/GodMode9i
*
* Changes against the source:
* - Documentation added
* - clean up / reorder
* - removed verbose output (must be handled on application level, not
* within the helpers */
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <assert.h>
/************************ Constants / Defines *********************************/
#define SECTOR_SIZE 0x200
#define MBR_PARTITIONS 4
#define MBR_BOOTSTRAP_SIZE (SECTOR_SIZE - (2 + MBR_PARTITIONS * 16))
#define MBR_SIGNATURE_0 0x55
#define MBR_SIGNATURE_1 0xAA
// https://3dbrew.org/wiki/NCSD#NCSD_header
#define NCSD_PARTITIONS 8
#define NCSD_SIGNATURESIZE 0x100
#define NCSD_HEADERSIZE (NCSD_SIGNATURESIZE + 0x060)
#define NCSD_MAGIC 0x4453434e
/************************ Structures / Datatypes ******************************/
#ifdef _MSC_VER
#pragma pack(push, 1)
#define __PACKED
#elif defined __GNUC__
#define __PACKED __attribute__ ((__packed__))
#endif
typedef struct {
uint32_t offset, length;
} __PACKED ncsd_partition_t;
typedef struct {
uint8_t digest[NCSD_SIGNATURESIZE];
} __PACKED ncsd_sginature;
typedef struct {
ncsd_sginature signature;
uint32_t magic, size;
uint64_t media_id;
uint8_t fs_types[NCSD_PARTITIONS], crypt_types[NCSD_PARTITIONS];
ncsd_partition_t partitions[NCSD_PARTITIONS];
} __PACKED ncsd_header_t;
/*
* CHS Sector Address Format
* See https://en.wikipedia.org/wiki/Master_boot_record#PTE
*
* ==========================================================
* | Offset | Field Length | Description |
* ==========================================================
* | 1 | 3 | CHS Address of first sector |
* | | | Bit 0..7: Head |
* | | | Bit 8..13: Sector |
* | | | Bit 14..23: Cylinder |
* ----------------------------------------------------------
*/
typedef struct {
uint8_t head;
uint8_t sectorAndCylHigh;
uint8_t cylinderLow;
} __PACKED chs_t;
/*
* Partition table entries
* See https://en.wikipedia.org/wiki/Master_boot_record#PTE
*
*
* ==========================================================
* | Offset | Field Length | Description |
* ==========================================================
* | 0 | 1 | Bit 7: Bootable |
* | | | Bit 0..6: Reserved (0) |
* ----------------------------------------------------------
* | 1 | 3 | CHS Address of first sector |
* | | | Bit 0..7: Head |
* | | | Bit 8..13: Sector |
* | | | Bit 14..23: Cylinder |
* ----------------------------------------------------------
* | 4 | 1 | Partition Type |
* ----------------------------------------------------------
* | 5 | 3 | CHS Address of last sector |
* | | | Bit 0..7: Head |
* | | | Bit 8..13: Sector |
* | | | Bit 14..23: Cylinder |
* ----------------------------------------------------------
* | 8 | 4 | LBA Address of first sector |
* ----------------------------------------------------------
* | 12 | 4 | Number of sectors |
* ----------------------------------------------------------
*/
typedef struct {
uint8_t status;
chs_t chs_first;
uint8_t type;
chs_t chs_last;
uint32_t offset;
uint32_t length;
} __PACKED mbr_partition_t;
/*
* Master Boot Record
* https://en.wikipedia.org/wiki/Master_boot_record#Sector_layout
*
* ==========================================================
* | Offset | Field Length | Description |
* ==========================================================
* | 0 | 446 | Boot code |
* ----------------------------------------------------------
* | 446 | 16 | Partition 0 |
* ----------------------------------------------------------
* | 462 | 16 | Partition 1 |
* ----------------------------------------------------------
* | 478 | 16 | Partition 2 |
* ----------------------------------------------------------
* | 494 | 16 | Partition 3 |
* ----------------------------------------------------------
* | 510 | 2 | Signature ( 55 aa ) |
* ----------------------------------------------------------
*/
typedef struct {
uint8_t bootstrap[MBR_BOOTSTRAP_SIZE];
mbr_partition_t partitions[MBR_PARTITIONS];
uint8_t boot_signature[2];
} __PACKED mbr_t;
#ifdef _MSC_VER
#pragma pack(pop)
#endif
#undef __PACKED
/************************ Function Protoypes **********************************/
int parse_ncsd(const uint8_t sector0[SECTOR_SIZE]);
int parse_mbr(const uint8_t sector0[SECTOR_SIZE], const int is3DS);
/************************ static code verification ****************************/
static_assert(sizeof(ncsd_header_t) == NCSD_HEADERSIZE, "sizeof(ncsd_header_t) should equal 0x160");
static_assert(sizeof(mbr_t) == SECTOR_SIZE, "sizeof(mbr_t) should equal 0x200");
#ifdef __cplusplus
}
#endif

132
arm9/src/nand/ticket0.h Normal file
View File

@ -0,0 +1,132 @@
#pragma once
#include <assert.h>
#include <stdint.h>
#ifdef _MSC_VER
#pragma pack(push, 1)
#define PACKED
#elif ! defined PACKED
#define PACKED __attribute__ ((__packed__))
#endif
#define RSA_2048_LEN (2048/8)
// most, if not all, are big endian
// http://problemkaputt.de/gbatek.htm#dsisdmmcdsiwareticketsandtitlemetadata
// http://dsibrew.org/wiki/Ticket
// http://wiibrew.org/wiki/Ticket
typedef struct {
uint8_t sig_type[4];
uint8_t sig[RSA_2048_LEN];
uint8_t padding0[0x3c];
char issuer[0x40];
uint8_t ecdh[0x3c];
uint8_t padding1[3];
uint8_t encrypted_title_key[0x10];
uint8_t unknown0;
uint8_t ticket_id[8];
uint8_t console_id[4];
uint8_t title_id[8];
uint8_t unknown1[2];
uint8_t version[2];
uint8_t permitted_titles_mask[4];
uint8_t permit_mask[4];
uint8_t title_export_allowed;
uint8_t common_key_index;
uint8_t unknown[0x30];
uint8_t content_access_permissions[0x40];
uint8_t padding2[2];
uint8_t time_limits[2 * 8 * sizeof(uint32_t)];
} PACKED ticket_v0_t;
static_assert(sizeof(ticket_v0_t) == 0x2a4, "invalid sizeof(ticket_v0_t)");
// http://dsibrew.org/wiki/Tmd
// http://wiibrew.org/wiki/Title_metadata
typedef struct {
uint8_t sig_type[4];
uint8_t sig[RSA_2048_LEN];
uint8_t padding0[0x3c];
char issuer[0x40];
uint8_t version;
uint8_t ca_crl_version;
uint8_t signer_crl_version;
uint8_t padding1;
uint8_t system_version[8];
uint8_t title_id[8];
uint8_t title_type[4];
uint8_t group_id[2];
uint8_t public_save_size[4];
uint8_t private_save_size[4];
uint8_t padding2[8];
uint8_t parent_control[0x10];
uint8_t padding3[0x1e];
uint8_t access_rights[4];
uint8_t title_version[2];
uint8_t num_content[2];
uint8_t boot_index[2];
uint8_t padding4[2];
} PACKED tmd_header_v0_t;
static_assert(sizeof(tmd_header_v0_t) == 0x1e4, "invalid sizeof(tmd_header_v0_t)");
typedef struct {
uint8_t content_id[4];
uint8_t index[2];
uint8_t type[2];
uint8_t size[8];
uint8_t sha1[20];
} PACKED tmd_content_v0_t;
static_assert(sizeof(tmd_content_v0_t) == 0x24, "invalid sizeof(tmd_contend_v0_t)");
// used in ticket encryption
// http://problemkaputt.de/gbatek.htm#dsiesblockencryption
#define AES_CCM_MAC_LEN 0x10
#define AES_CCM_NONCE_LEN 0x0c
typedef struct {
uint8_t ccm_mac[AES_CCM_MAC_LEN];
union {
struct {
uint8_t fixed_3a;
uint8_t nonce[AES_CCM_NONCE_LEN];
uint8_t len24be[3];
};
struct {
uint8_t padding[AES_CCM_NONCE_LEN];
// defined for convenience, it's still big endian with only 24 effective bits
// read it as 32 bit big endian and discard the highest 8 bits
uint8_t len32be[4];
};
uint8_t encrypted[0x10];
};
} PACKED es_block_footer_t;
static_assert(sizeof(es_block_footer_t) == 0x20, "invalid sizeof(es_block_footer_t)");
// used in cert.sys
// http://problemkaputt.de/gbatek.htm#dsisdmmcfirmwaredevkpandcertsyscertificatefiles
// "DSi SD/MMC Firmware dev.kp and cert.sys Certificate Files"
typedef struct {
uint32_t signature_type;
uint8_t signature[RSA_2048_LEN];
uint8_t padding0[0x3c];
char signature_name[0x40];
uint32_t key_type;
char key_name[0x40];
uint32_t key_flags;
uint8_t rsa_key[RSA_2048_LEN];
uint8_t rsa_exp[4];
uint8_t padding1[0x34];
} PACKED cert_t;
static_assert(sizeof(cert_t) == 0x300, "invalid sizeof(cert_t)");
#ifdef _MSC_VER
#pragma pack(pop)
#endif
#undef PACKED

343
arm9/src/nand/twltool/dsi.c Normal file
View File

@ -0,0 +1,343 @@
#include "dsi.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "u128_math.h"
void dsi_set_key(dsi_context* ctx, const unsigned char key[16])
{
unsigned char keyswap[16];
u128_swap(keyswap, key);
aes_setkey_enc(&ctx->aes, keyswap, 128);
}
void dsi_add_ctr(dsi_context* ctx, unsigned int carry)
{
unsigned int counter[4];
unsigned char *outctr = (unsigned char*)ctx->ctr;
int sum;
signed int i;
for (i = 0; i < 4; i++)
counter[i] = (outctr[i * 4 + 0] << 24) | (outctr[i * 4 + 1] << 16) |
(outctr[i * 4 + 2] << 8) | (outctr[i * 4 + 3] << 0);
for (i = 3; i >= 0; i--)
{
sum = counter[i] + carry;
if (sum < counter[i])
carry = 1;
else
carry = 0;
counter[i] = sum;
}
for (i = 0; i < 4; i++)
{
outctr[i * 4 + 0] = counter[i] >> 24;
outctr[i * 4 + 1] = counter[i] >> 16;
outctr[i * 4 + 2] = counter[i] >> 8;
outctr[i * 4 + 3] = counter[i] >> 0;
}
}
void dsi_set_ctr(dsi_context* ctx, const unsigned char ctr[16])
{
int i;
for (i=0; i<16; i++)
ctx->ctr[i] = ctr[15-i];
}
void dsi_init_ctr(dsi_context* ctx, const unsigned char key[16], const unsigned char ctr[12])
{
dsi_set_key(ctx, key);
dsi_set_ctr(ctx, ctr);
}
void dsi_crypt_ctr(dsi_context* ctx, const void* in, void* out, unsigned int len)
{
unsigned int i;
for (i = 0; i < len; i += 0x10)
{
dsi_crypt_ctr_block(ctx, in+i, out+i);
}
}
void dsi_crypt_ctr_block(dsi_context* ctx, const unsigned char input[16], unsigned char output[16])
{
int i;
unsigned char stream[16];
aes_crypt_ecb(&ctx->aes, AES_ENCRYPT, ctx->ctr, stream);
if (input)
{
for (i=0; i<16; i++)
{
output[i] = stream[15-i] ^ input[i];
}
}
else
{
for (i=0; i<16; i++)
output[i] = stream[15-i];
}
dsi_add_ctr(ctx, 1);
}
void dsi_init_ccm(dsi_context* ctx, unsigned char key[16], unsigned int maclength,
unsigned int payloadlength, unsigned int assoclength, unsigned char nonce[12])
{
int i;
dsi_set_key(ctx, key);
ctx->maclen = maclength;
maclength = (maclength-2)/2;
payloadlength = (payloadlength+15) & ~15;
// CCM B0 block:
// [1-byte flags] [12-byte nonce] [3-byte size]
ctx->mac[0] = (maclength<<3) | 2;
if (assoclength)
ctx->mac[0] |= (1<<6);
for (i=0; i<12; i++)
ctx->mac[1+i] = nonce[11-i];
ctx->mac[13] = payloadlength>>16;
ctx->mac[14] = payloadlength>>8;
ctx->mac[15] = payloadlength>>0;
aes_crypt_ecb(&ctx->aes, AES_ENCRYPT, ctx->mac, ctx->mac);
// CCM CTR:
// [1-byte flags] [12-byte nonce] [3-byte ctr]
ctx->ctr[0] = 2;
for (i=0; i<12; i++)
ctx->ctr[1+i] = nonce[11-i];
ctx->ctr[13] = 0;
ctx->ctr[14] = 0;
ctx->ctr[15] = 0;
dsi_crypt_ctr_block(ctx, 0, ctx->S0);
}
void dsi_encrypt_ccm_block(dsi_context* ctx, unsigned char input[16], unsigned char output[16], unsigned char* mac)
{
int i;
for (i=0; i<16; i++)
ctx->mac[i] ^= input[15-i];
aes_crypt_ecb(&ctx->aes, AES_ENCRYPT, ctx->mac, ctx->mac);
if (mac)
{
for (i=0; i<16; i++)
mac[i] = ctx->mac[15-i] ^ ctx->S0[i];
}
if (output)
dsi_crypt_ctr_block(ctx, input, output);
}
void dsi_decrypt_ccm_block(dsi_context* ctx, unsigned char input[16], unsigned char output[16], unsigned char* mac)
{
int i;
if (output)
{
dsi_crypt_ctr_block(ctx, input, output);
for (i=0; i<16; i++)
ctx->mac[i] ^= output[15-i];
}
else
{
for (i=0; i<16; i++)
ctx->mac[i] ^= input[15-i];
}
aes_crypt_ecb(&ctx->aes, AES_ENCRYPT, ctx->mac, ctx->mac);
if (mac)
{
for (i=0; i<16; i++)
mac[i] = ctx->mac[15-i] ^ ctx->S0[i];
}
}
void dsi_decrypt_ccm(dsi_context* ctx, unsigned char* input, unsigned char* output, unsigned int size, unsigned char* mac)
{
unsigned char block[16];
unsigned char ctr[16];
while (size > 16)
{
dsi_decrypt_ccm_block(ctx, input, output, mac);
if (input)
input += 16;
if (output)
output += 16;
size -= 16;
}
memcpy(ctr, ctx->ctr, 16);
memset(block, 0, 16);
dsi_crypt_ctr_block(ctx, block, block);
memcpy(ctx->ctr, ctr, 16);
memcpy(block, input, size);
dsi_decrypt_ccm_block(ctx, block, block, mac);
memcpy(output, block, size);
}
void dsi_encrypt_ccm(dsi_context* ctx, unsigned char* input, unsigned char* output, unsigned int size, unsigned char* mac)
{
unsigned char block[16];
while (size > 16)
{
dsi_encrypt_ccm_block(ctx, input, output, mac);
if (input)
input += 16;
if (output)
output += 16;
size -= 16;
}
memset(block, 0, 16);
memcpy(block, input, size);
dsi_encrypt_ccm_block(ctx, block, block, mac);
memcpy(output, block, size);
}
void dsi_es_init(dsi_es_context* ctx, unsigned char key[16])
{
memcpy(ctx->key, key, 16);
ctx->randomnonce = 1;
}
void dsi_es_set_nonce(dsi_es_context* ctx, unsigned char nonce[12])
{
memcpy(ctx->nonce, nonce, 12);
ctx->randomnonce = 0;
}
void dsi_es_set_random_nonce(dsi_es_context* ctx)
{
ctx->randomnonce = 1;
}
int dsi_es_decrypt(dsi_es_context* ctx, unsigned char* buffer, unsigned char metablock[32], unsigned int size)
{
unsigned char ctr[16];
unsigned char nonce[12];
unsigned char scratchpad[16];
unsigned char chkmac[16];
unsigned char genmac[16];
dsi_context cryptoctx;
unsigned int chksize;
memcpy(chkmac, metablock, 16);
memcpy(ctr, metablock + 16, 16);
ctr[0] = 0;
ctr[13] = 0;
ctr[14] = 0;
ctr[15] = 0;
dsi_init_ctr(&cryptoctx, ctx->key, ctr);
dsi_crypt_ctr_block(&cryptoctx, metablock+16, scratchpad);
chksize = (scratchpad[13]<<16) | (scratchpad[14]<<8) | (scratchpad[15]<<0);
if (scratchpad[0] != 0x3A)
{
return -1;
}
if (chksize != size)
{
return -2;
}
memcpy(nonce, metablock + 17, 12);
dsi_init_ccm(&cryptoctx, ctx->key, 16, size, 0, nonce);
dsi_decrypt_ccm(&cryptoctx, buffer, buffer, size, genmac);
if (memcmp(genmac, chkmac, 16) != 0)
{
return -3;
}
return 0;
}
void dsi_es_encrypt(dsi_es_context* ctx, unsigned char* buffer, unsigned char metablock[32], unsigned int size)
{
int i;
unsigned char nonce[12];
unsigned char mac[16];
unsigned char ctr[16];
unsigned char scratchpad[16];
dsi_context cryptoctx;
if (ctx->randomnonce)
{
srand((unsigned int)time(0));
for (i=0; i<12; i++)
nonce[i] = rand();
}
else
{
memcpy(nonce, ctx->nonce, 12);
}
dsi_init_ccm(&cryptoctx, ctx->key, 16, size, 0, nonce);
dsi_encrypt_ccm(&cryptoctx, buffer, buffer, size, mac);
memset(scratchpad, 0, 16);
scratchpad[0] = 0x3A;
scratchpad[13] = size >> 16;
scratchpad[14] = size >> 8;
scratchpad[15] = size >> 0;
memset(ctr, 0, 16);
memcpy(ctr+1, nonce, 12);
dsi_init_ctr(&cryptoctx, ctx->key, ctr);
dsi_crypt_ctr_block(&cryptoctx, scratchpad, metablock+16);
memcpy(metablock+17, nonce, 12);
memcpy(metablock, mac, 16);
}

View File

@ -0,0 +1,68 @@
#ifndef _DSI_H_
#define _DSI_H_
#include "polarssl/aes.h"
typedef struct
{
unsigned char ctr[16];
unsigned char mac[16];
unsigned char S0[16];
unsigned int maclen;
aes_context aes;
}
dsi_context;
typedef struct
{
unsigned char key[16];
unsigned char nonce[12];
int randomnonce;
} dsi_es_context;
#ifdef __cplusplus
extern "C" {
#endif
void dsi_set_key(dsi_context* ctx, const unsigned char key[16]);
void dsi_add_ctr(dsi_context* ctx, unsigned int carry);
void dsi_set_ctr(dsi_context* ctx, const unsigned char ctr[16]);
void dsi_init_ctr(dsi_context* ctx, const unsigned char key[16], const unsigned char ctr[12]);
void dsi_crypt_ctr(dsi_context* ctx, const void* in, void* out, unsigned int len);
void dsi_crypt_ctr_block(dsi_context* ctx, const unsigned char input[16], unsigned char output[16]);
void dsi_init_ccm(dsi_context* ctx, unsigned char key[16], unsigned int maclength,
unsigned int payloadlength, unsigned int assoclength, unsigned char nonce[12]);
void dsi_encrypt_ccm_block(dsi_context* ctx, unsigned char input[16], unsigned char output[16], unsigned char* mac);
void dsi_decrypt_ccm_block(dsi_context* ctx, unsigned char input[16], unsigned char output[16], unsigned char* mac);
void dsi_decrypt_ccm(dsi_context* ctx, unsigned char* input, unsigned char* output, unsigned int size, unsigned char* mac);
void dsi_encrypt_ccm(dsi_context* ctx, unsigned char* input, unsigned char* output, unsigned int size, unsigned char* mac);
void dsi_es_init(dsi_es_context* ctx, unsigned char key[16]);
void dsi_es_set_nonce(dsi_es_context* ctx, unsigned char nonce[12]);
void dsi_es_set_random_nonce(dsi_es_context* ctx);
int dsi_es_decrypt(dsi_es_context* ctx, unsigned char* buffer, unsigned char metablock[32], unsigned int size);
void dsi_es_encrypt(dsi_es_context* ctx, unsigned char* buffer, unsigned char metablock[32], unsigned int size);
#ifdef __cplusplus
}
#endif
#endif // _DSI_H_

106
arm9/src/nand/u128_math.c Normal file
View File

@ -0,0 +1,106 @@
// u128_math
#include "u128_math.h"
#include <string.h>
// rotate a 128bit, little endian by shift bits in direction of increasing significance.
void u128_lrot(uint8_t *num, uint32_t shift)
{
uint8_t tmp[16];
for (int i=0;i<16;i++)
{
// rot: rotate to more significant.
// LSB is tmp[0], MSB is tmp[15]
const uint32_t byteshift = shift / 8;
const uint32_t bitshift = shift % 8;
tmp[(i+byteshift) % 16] = (num[i] << bitshift)
| ((num[(i+16-1) % 16] >> (8-bitshift)) & 0xff);
}
memcpy(num, tmp, 16);
}
// rotate a 128bit, little endian by shift bits in direction of decreasing significance.
void u128_rrot(uint8_t *num, uint32_t shift)
{
uint8_t tmp[16];
for (int i=0;i<16;i++)
{
// rot: rotate to less significant.
// LSB is tmp[0], MSB is tmp[15]
const uint32_t byteshift = shift / 8;
const uint32_t bitshift = shift % 8;
tmp[i] = (num[(i+byteshift) % 16] >> bitshift)
| ((num[(i+byteshift+1) % 16] << (8-bitshift)) & 0xff);
}
memcpy(num, tmp, 16);
}
// xor two 128bit, little endian values and store the result into the first
void u128_xor(uint8_t *a, const uint8_t *b)
{
for (int i=0;i<16;i++)
{
a[i] = a[i] ^ b[i];
}
}
// or two 128bit, little endian values and store the result into the first
void u128_or(uint8_t *a, const uint8_t *b)
{
for (int i=0;i<16;i++)
{
a[i] = a[i] | b[i];
}
}
// and two 128bit, little endian values and store the result into the first
void u128_and(uint8_t *a, const uint8_t *b)
{
for (int i=0;i<16;i++)
{
a[i] = a[i] & b[i];
}
}
// add two 128bit, little endian values and store the result into the first
void u128_add(uint8_t *a, const uint8_t *b)
{
uint8_t carry = 0;
for (int i=0;i<16;i++)
{
uint16_t sum = a[i] + b[i] + carry;
a[i] = sum & 0xff;
carry = sum >> 8;
}
}
// add two 128bit, little endian values and store the result into the first
void u128_add32(uint8_t *a, const uint32_t b)
{
uint8_t _b[16];
memset(_b, 0, sizeof(_b));
for (int i=0;i<4;i++)
_b[i] = b >> (i*8);
u128_add(a, _b);
}
// sub two 128bit, little endian values and store the result into the first
void u128_sub(uint8_t *a, const uint8_t *b)
{
uint8_t carry = 0;
for (int i=0;i<16;i++)
{
uint16_t sub = a[i] - b[i] - (carry & 1);
a[i] = sub & 0xff;
carry = sub >> 8;
}
}
void u128_swap(uint8_t *out, const uint8_t *in)
{
for (int i=0;i<16;i++)
{
out[15-i] = in[i];
}
}

38
arm9/src/nand/u128_math.h Normal file
View File

@ -0,0 +1,38 @@
// u128_math
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
// left rotate by (shift % 128) bits
void u128_lrot(uint8_t *num, uint32_t shift);
// right rotate by (shift % 128) bits
void u128_rrot(uint8_t *num, uint32_t shift);
// bitwise xor strored to a
void u128_xor(uint8_t *a, const uint8_t *b);
// bitwise or strored to a
void u128_or(uint8_t *a, const uint8_t *b);
// bitwise and strored to a
void u128_and(uint8_t *a, const uint8_t *b);
// add b to a and store in a
void u128_add(uint8_t *a, const uint8_t *b);
// add 32 bit value b to a and store in a
void u128_add32(uint8_t *a, const uint32_t b);
// substract b from a and store in a
void u128_sub(uint8_t *a, const uint8_t *b);
// swap byte order
void u128_swap(uint8_t *out, const uint8_t *in);
#ifdef __cplusplus
}
#endif

296
arm9/src/rom.c Normal file
View File

@ -0,0 +1,296 @@
#include "rom.h"
#include "main.h"
#include "storage.h"
#include <dirent.h>
#include <nds.h>
#include <malloc.h>
#include <stdio.h>
tDSiHeader* getRomHeader(char const* fpath)
{
if (!fpath) return NULL;
tDSiHeader* h = NULL;
FILE* f = fopen(fpath, "rb");
if (f)
{
h = (tDSiHeader*)malloc(sizeof(tDSiHeader));
if (h)
{
if (romIsCia(fpath))
fseek(f, 0x3900, SEEK_SET);
else
fseek(f, 0, SEEK_SET);
fread(h, sizeof(tDSiHeader), 1, f);
}
fclose(f);
}
return h;
}
tNDSBanner* getRomBanner(char const* fpath)
{
if (!fpath) return NULL;
tDSiHeader* h = getRomHeader(fpath);
tNDSBanner* b = NULL;
if (h)
{
FILE* f = fopen(fpath, "rb");
if (f)
{
b = (tNDSBanner*)malloc(sizeof(tNDSBanner));
if (b)
{
if (romIsCia(fpath))
fseek(f, 0x3900, SEEK_SET);
else
fseek(f, 0, SEEK_SET);
fseek(f, h->ndshdr.bannerOffset, SEEK_CUR);
fread(b, sizeof(tNDSBanner), 1, f);
}
}
free(h);
fclose(f);
}
return b;
}
bool getGameTitle(tNDSBanner* b, char* out, bool full)
{
if (!b) return false;
if (!out) return false;
//get system language
int lang = PersonalData->language;
//not japanese or chinese
if (lang == 0 || lang == 6)
lang = 1;
//read title
u16 c;
for (int i = 0; i < 128; i++)
{
c = b->titles[lang][i];
//remove accents
if (c == 0x00F3)
c = 'o';
if (c == 0x00E1)
c = 'a';
out[i] = (char)c;
if (!full && out[i] == '\n')
{
out[i] = '\0';
break;
}
}
out[128] = '\0';
return true;
}
bool getGameTitlePath(char const* fpath, char* out, bool full)
{
if (!fpath) return false;
if (!out) return false;
tNDSBanner* b = getRomBanner(fpath);
bool result = getGameTitle(b, out, full);
free(b);
return result;
}
bool getRomLabel(tDSiHeader* h, char* out)
{
if (!h) return false;
if (!out) return false;
sprintf(out, "%.12s", h->ndshdr.gameTitle);
return true;
}
bool getRomCode(tDSiHeader* h, char* out)
{
if (!h) return false;
if (!out) return false;
sprintf(out, "%.4s", h->ndshdr.gameCode);
return true;
}
void printRomInfo(char const* fpath)
{
clearScreen(&topScreen);
if (!fpath) return;
tDSiHeader* h = getRomHeader(fpath);
tNDSBanner* b = getRomBanner(fpath);
if (!isDsiHeader(h))
{
iprintf("Could not read dsi header.\n");
}
else
{
if (!b)
{
iprintf("Could not read banner.\n");
}
else
{
//proper title
{
char gameTitle[128+1];
getGameTitle(b, gameTitle, true);
iprintf("%s\n\n", gameTitle);
}
//file size
{
iprintf("Size: ");
unsigned long long romSize = getRomSize(fpath);
printBytes(romSize);
//size in blocks, rounded up
iprintf(" (%lld blocks)\n", ((romSize / BYTES_PER_BLOCK) * BYTES_PER_BLOCK + BYTES_PER_BLOCK) / BYTES_PER_BLOCK);
}
iprintf("Label: %.12s\n", h->ndshdr.gameTitle);
iprintf("Game Code: %.4s\n", h->ndshdr.gameCode);
//system type
{
iprintf("Unit Code: ");
switch (h->ndshdr.unitCode)
{
case 0: iprintf("NDS"); break;
case 2: iprintf("NDS+DSi"); break;
case 3: iprintf("DSi"); break;
default: iprintf("unknown");
}
iprintf("\n");
}
//application type
{
iprintf("Program Type: ");
switch (h->ndshdr.reserved1[7])
{
case 0x3: iprintf("Normal"); break;
case 0xB: iprintf("Sys"); break;
case 0xF: iprintf("Debug/Sys"); break;
default: iprintf("unknown");
}
iprintf("\n");
}
//DSi title ids
{
if (h->tid_high == 0x00030004 ||
h->tid_high == 0x00030005 ||
h->tid_high == 0x00030011 || // TID for software in TWL SDK
h->tid_high == 0x00030015 ||
h->tid_high == 0x00030017 ||
h->tid_high == 0x00030000)
{
iprintf("Title ID: %08x %08x\n", (unsigned int)h->tid_high, (unsigned int)h->tid_low);
}
}
//print full file path
iprintf("\n%s\n", fpath);
//print extra files
int extensionPos = strrchr(fpath, '.') - fpath;
char temp[PATH_MAX];
strcpy(temp, fpath);
strcpy(temp + extensionPos, ".tmd");
//DSi TMDs are 520, TMDs from NUS are 2,312. If 2,312 we can simply trim it to 520
int tmdSize = getFileSizePath(temp);
if (access(temp, F_OK) == 0)
printf("\t\x1B[%om%s\n\x1B[47m", (tmdSize == 520 || tmdSize == 2312) ? 047 : 041, strrchr(temp, '/') + 1);
strcpy(temp + extensionPos, ".pub");
if (access(temp, F_OK) == 0)
printf("\t\x1B[%om%s\n\x1B[47m", (getFileSizePath(temp) == h->public_sav_size) ? 047 : 041, strrchr(temp, '/') + 1);
strcpy(temp + extensionPos, ".prv");
if (access(temp, F_OK) == 0)
printf("\t\x1B[%om%s\n\x1B[47m", (getFileSizePath(temp) == h->private_sav_size) ? 047 : 041, strrchr(temp, '/') + 1);
strcpy(temp + extensionPos, ".bnr");
if (access(temp, F_OK) == 0)
printf("\t\x1B[%om%s\n\x1B[47m", (getFileSizePath(temp) == 0x4000) ? 047 : 041, strrchr(temp, '/') + 1);
}
}
free(b);
free(h);
}
unsigned long long getRomSize(char const* fpath)
{
if (!fpath) return 0;
unsigned long long size = 0;
FILE* f = fopen(fpath, "rb");
if (f)
{
//cia
if (romIsCia(fpath))
{
unsigned char bytes[4] = { 0 };
fseek(f, 0x38D0, SEEK_SET);
fread(bytes, 4, 1, f);
size = (bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3];
}
else
{
fseek(f, 0, SEEK_END);
size = ftell(f);
}
}
fclose(f);
return size;
}
bool romIsCia(char const* fpath)
{
if (!fpath) return false;
return (strstr(fpath, ".cia") != NULL || strstr(fpath, ".CIA") != NULL);
}
bool isDsiHeader(tDSiHeader* h)
{
if (!h) return false;
u16 crc16 = swiCRC16(0xFFFF, h, 0x15E);
return h->ndshdr.headerCRC16 == crc16;
}

23
arm9/src/rom.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef ROM_H
#define ROM_H
#include <nds/ndstypes.h>
#include <nds/memory.h>
tDSiHeader* getRomHeader(char const* fpath);
tNDSBanner* getRomBanner(char const* fpath);
bool getGameTitle(tNDSBanner* b, char* out, bool full);
bool getGameTitlePath(char const* fpath, char* out, bool full);
bool getRomLabel(tDSiHeader* h, char* out);
bool getRomCode(tDSiHeader* h, char* out);
void printRomInfo(char const* fpath);
unsigned long long getRomSize(char const* fpath);
bool romIsCia(char const* fpath);
bool isDsiHeader(tDSiHeader* h);
#endif

93
arm9/src/sav.c Normal file
View File

@ -0,0 +1,93 @@
#include "sav.h"
#include <string.h>
#include <malloc.h>
#define align(v, a) (((v) % (a)) ? ((v) + (a) - ((v) % (a))) : (v))
bool initFatHeader(FILE* f)
{
if (!f)
return false;
//get size
fseek(f, 0, SEEK_END);
u32 size = ftell(f);
//based on GodMode9
//https://github.com/d0k3/GodMode9/blob/d8d43c14f3317423c677b1c7e0987bcb9bbd7299/arm9/source/game/nds.c#L47-L105
const u16 sectorSize = 0x200;
//fit maximum sectors for the size
const u16 maxSectors = size / sectorSize;
u16 sectorCount = 1;
u16 secPerTrk = 1;
u16 numHeads = 1;
u16 sectorCountNext = 0;
while (sectorCountNext <= maxSectors)
{
sectorCountNext = secPerTrk * (numHeads + 1) * (numHeads + 1);
if (sectorCountNext <= maxSectors)
{
numHeads++;
sectorCount = sectorCountNext;
secPerTrk++;
sectorCountNext = secPerTrk * numHeads * numHeads;
if (sectorCountNext <= maxSectors)
{
sectorCount = sectorCountNext;
}
}
}
sectorCountNext = (secPerTrk + 1) * numHeads * numHeads;
if (sectorCountNext <= maxSectors)
{
secPerTrk++;
sectorCount = sectorCountNext;
}
u8 secPerCluster = (sectorCount > (8 << 10)) ? 8 : (sectorCount > (1 << 10) ? 4 : 1);
u16 rootEntryCount = size < 0x8C000 ? 0x20 : 0x200;
u16 totalClusters = align(sectorCount, secPerCluster) / secPerCluster;
u32 fatBytes = (align(totalClusters, 2) / 2) * 3; // 2 sectors -> 3 byte
u16 fatSize = align(fatBytes, sectorSize) / sectorSize;
FATHeader* h = (FATHeader*)malloc(sizeof(FATHeader));
h->BS_JmpBoot[0] = 0xE9;
h->BS_JmpBoot[1] = 0;
h->BS_JmpBoot[2] = 0;
memcpy(h->BS_OEMName, "MSWIN4.1", 8);
h->BPB_BytesPerSec = sectorSize;
h->BPB_SecPerClus = secPerCluster;
h->BPB_RsvdSecCnt = 0x0001;
h->BPB_NumFATs = 0x02;
h->BPB_RootEntCnt = rootEntryCount;
h->BPB_TotSec16 = sectorCount;
h->BPB_Media = 0xF8; // "hard drive"
h->BPB_FATSz16 = fatSize;
h->BPB_SecPerTrk = secPerTrk;
h->BPB_NumHeads = numHeads;
h->BPB_HiddSec = 0x00000000;
h->BPB_TotSec32 = 0x00000000;
h->BS_DrvNum = 0x05;
h->BS_Reserved1 = 0x00;
h->BS_BootSig = 0x29;
h->BS_VolID = 0x12345678;
memcpy(h->BS_VolLab, "VOLUMELABEL", 11);
memcpy(h->BS_FilSysType,"FAT12 ", 8);
memset(h->BS_BootCode, 0, sizeof(h->BS_BootCode));
h->BS_BootSign = 0xAA55;
fseek(f, 0, SEEK_SET);
fwrite(h, sizeof(FATHeader), 1, f);
free(h);
return true;
}

38
arm9/src/sav.h Normal file
View File

@ -0,0 +1,38 @@
#ifndef SAV_H
#define SAV_H
#include <nds/ndstypes.h>
#include <stdio.h>
//http://elm-chan.org/docs/fat_e.html
#pragma pack(push, 1)
typedef struct
{
u8 BS_JmpBoot[3]; //0x0000
u8 BS_OEMName[8]; //0x0003
u16 BPB_BytesPerSec; //0x000B
u8 BPB_SecPerClus; //0x000D
u16 BPB_RsvdSecCnt; //0x000E
u8 BPB_NumFATs;
u16 BPB_RootEntCnt;
u16 BPB_TotSec16;
u8 BPB_Media;
u16 BPB_FATSz16;
u16 BPB_SecPerTrk;
u16 BPB_NumHeads;
u32 BPB_HiddSec;
u32 BPB_TotSec32;
u8 BS_DrvNum;
u8 BS_Reserved1;
u8 BS_BootSig;
u32 BS_VolID;
u8 BS_VolLab[11];
u8 BS_FilSysType[8];
u8 BS_BootCode[448];
u16 BS_BootSign;
} FATHeader;
#pragma pack(push, 0)
bool initFatHeader(FILE* f);
#endif

564
arm9/src/storage.c Normal file
View File

@ -0,0 +1,564 @@
#include "storage.h"
#include "main.h"
#include "message.h"
#include <errno.h>
#include <dirent.h>
#define TITLE_LIMIT 39
//printing
void printBytes(unsigned long long bytes)
{
if (bytes < 1024)
iprintf("%dB", (unsigned int)bytes);
else if (bytes < 1024 * 1024)
printf("%.2fKB", (float)bytes / 1024.f);
else if (bytes < 1024 * 1024 * 1024)
printf("%.2fMB", (float)bytes / 1024.f / 1024.f);
else
printf("%.2fGB", (float)bytes / 1024.f / 1024.f / 1024.f);
}
//progress bar
static int lastBars = 0;
void printProgressBar(float percent)
{
if (percent < 0.f) percent = 0.f;
if (percent > 1.f) percent = 1.f;
int bars = (int)(30.f * percent);
//skip redundant prints
if (bars != lastBars)
{
consoleSelect(&topScreen);
iprintf("\x1B[42m"); //green
//Print frame
if (lastBars <= 0)
{
iprintf("\x1b[23;0H[");
iprintf("\x1b[23;31H]");
}
//Print bars
if (bars > 0)
{
for (int i = 0; i < bars; i++)
iprintf("\x1b[23;%dH|", 1 + i);
}
lastBars = bars;
iprintf("\x1B[47m"); //white
}
}
void clearProgressBar()
{
lastBars = 0;
consoleSelect(&topScreen);
iprintf("\x1b[23;0H ");
}
//files
bool fileExists(char const* path)
{
if (!path) return false;
FILE* f = fopen(path, "rb");
if (!f)
return false;
fclose(f);
return true;
}
int copyFile(char const* src, char const* dst)
{
if (!src) return 1;
unsigned long long size = getFileSizePath(src);
return copyFilePart(src, 0, size, dst);
}
int copyFilePart(char const* src, u32 offset, u32 size, char const* dst)
{
if (!src) return 1;
if (!dst) return 2;
FILE* fin = fopen(src, "rb");
if (!fin)
{
fclose(fin);
return 3;
}
else
{
if (fileExists(dst))
remove(dst);
FILE* fout = fopen(dst, "wb");
if (!fout)
{
fclose(fin);
fclose(fout);
return 4;
}
else
{
fseek(fin, offset, SEEK_SET);
consoleSelect(&topScreen);
int bytesRead;
unsigned long long totalBytesRead = 0;
#define BUFF_SIZE 128 //Arbitrary. A value too large freezes the ds.
char* buffer = (char*)malloc(BUFF_SIZE);
while (!programEnd)
{
unsigned int toRead = BUFF_SIZE;
if (size - totalBytesRead < BUFF_SIZE)
toRead = size - totalBytesRead;
bytesRead = fread(buffer, 1, toRead, fin);
fwrite(buffer, bytesRead, 1, fout);
totalBytesRead += bytesRead;
printProgressBar( ((float)totalBytesRead / (float)size) );
if (bytesRead != BUFF_SIZE)
break;
}
clearProgressBar();
consoleSelect(&bottomScreen);
free(buffer);
}
fclose(fout);
}
fclose(fin);
return 0;
}
unsigned long long getFileSize(FILE* f)
{
if (!f) return 0;
fseek(f, 0, SEEK_END);
unsigned long long size = ftell(f);
fseek(f, 0, SEEK_SET);
return size;
}
unsigned long long getFileSizePath(char const* path)
{
if (!path) return 0;
FILE* f = fopen(path, "rb");
unsigned long long size = getFileSize(f);
fclose(f);
return size;
}
bool padFile(char const* path, int size)
{
if (!path) return false;
FILE* f = fopen(path, "ab");
if (!f)
{
return false;
}
else
{
for (int i = 0; i < size; i++)
fputc('\0', f);
}
fclose(f);
return true;
}
//directories
bool dirExists(char const* path)
{
if (!path) return false;
DIR* dir = opendir(path);
if (!dir)
return false;
closedir(dir);
return true;
}
bool copyDir(char const* src, char const* dst)
{
if (!src || !dst) return false;
// iprintf("copyDir\n%s\n%s\n\n", src, dst);
bool result = true;
DIR* dir = opendir(src);
struct dirent* ent;
if (!dir)
{
return false;
}
else
{
while ( (ent = readdir(dir)) )
{
if (strcmp(".", ent->d_name) == 0 || strcmp("..", ent->d_name) == 0)
continue;
if (ent->d_type == DT_DIR)
{
char* dsrc = (char*)malloc(strlen(src) + strlen(ent->d_name) + 4);
sprintf(dsrc, "%s/%s", src, ent->d_name);
char* ddst = (char*)malloc(strlen(dst) + strlen(ent->d_name) + 4);
sprintf(ddst, "%s/%s", dst, ent->d_name);
mkdir(ddst, 0777);
if (!copyDir(dsrc, ddst))
result = false;
free(ddst);
free(dsrc);
}
else
{
char* fsrc = (char*)malloc(strlen(src) + strlen(ent->d_name) + 4);
sprintf(fsrc, "%s/%s", src, ent->d_name);
char* fdst = (char*)malloc(strlen(dst) + strlen(ent->d_name) + 4);
sprintf(fdst, "%s/%s", dst, ent->d_name);
// iprintf("%s\n%s\n\n", fsrc, fdst);
iprintf("%s -> \n%s...", fsrc, fdst);
int ret = copyFile(fsrc, fdst);
if (ret != 0)
{
iprintf("\x1B[31m"); //red
iprintf("Fail\n");
iprintf("\x1B[33m"); //yellow
iprintf("%s\n", strerror(errno));
/*
switch (ret)
{
case 1:
iprintf("Empty input path.\n");
break;
case 2:
iprintf("Empty output path.\n");
break;
case 3:
iprintf("Error opening input file.\n");
break;
case 4:
iprintf("Error opening output file.\n");
break;
}
*/
iprintf("\x1B[47m"); //white
result = false;
}
else
{
iprintf("\x1B[42m"); //green
iprintf("Done\n");
iprintf("\x1B[47m"); //white
}
free(fdst);
free(fsrc);
}
}
}
closedir(dir);
return result;
}
bool deleteDir(char const* path)
{
if (!path) return false;
if (strcmp("/", path) == 0)
{
//oh fuck no
return false;
}
bool result = true;
DIR* dir = opendir(path);
struct dirent* ent;
if (!dir)
{
result = false;
}
else
{
while ( (ent = readdir(dir)) )
{
if (strcmp(".", ent->d_name) == 0 || strcmp("..", ent->d_name) == 0)
continue;
if (ent->d_type == DT_DIR)
{
//Delete directory
char subpath[512];
sprintf(subpath, "%s/%s", path, ent->d_name);
if (!deleteDir(subpath))
result = false;
}
else
{
//Delete file
char fpath[512];
sprintf(fpath, "%s/%s", path, ent->d_name);
iprintf("%s...", fpath);
if (remove(fpath) != 0)
{
iprintf("\x1B[31m");
iprintf("Fail\n");
iprintf("\x1B[47m");
result = false;
}
else
{
iprintf("\x1B[42m");
iprintf("Done\n");
iprintf("\x1B[47m");
}
}
}
}
closedir(dir);
iprintf("%s...", path);
if (remove(path) != 0)
{
iprintf("\x1B[31m");
iprintf("Fail\n");
iprintf("\x1B[47m");
result = false;
}
else
{
iprintf("\x1B[42m");
iprintf("Done\n");
iprintf("\x1B[47m");
}
return result;
}
unsigned long long getDirSize(const char* path, u32 blockSize)
{
if (!path) return 0;
unsigned long long size = 0;
DIR* dir = opendir(path);
struct dirent* ent;
if (dir)
{
while ((ent = readdir(dir)))
{
if (strcmp(".", ent->d_name) == 0 || strcmp("..", ent->d_name) == 0)
continue;
if (ent->d_type == DT_DIR)
{
char fullpath[512];
sprintf(fullpath, "%s/%s", path, ent->d_name);
size += getDirSize(fullpath, blockSize);
}
else
{
char fullpath[260];
sprintf(fullpath, "%s/%s", path, ent->d_name);
size += getFileSizePath(fullpath);
// If we've specified a block size, round up to it
if ((size % blockSize) != 0)
size += blockSize - (size % blockSize);
}
}
}
closedir(dir);
return size;
}
//home menu
int getMenuSlots()
{
//Assume the home menu has a hard limit on slots
//Find a better way to do this
return TITLE_LIMIT;
}
int getMenuSlotsFree()
{
//Get number of open menu slots by subtracting the number of directories in the title folders
//Find a better way to do this
const int NUM_OF_DIRS = 4;
const char* dirs[] = {
"00030004",
"00030005",
"00030015",
"00030017"
};
int freeSlots = getMenuSlots();
DIR* dir;
struct dirent* ent;
for (int i = 0; i < NUM_OF_DIRS; i++)
{
char path[256];
sprintf(path, "%s:/title/%s", sdnandMode ? "sd" : "nand", dirs[i]);
dir = opendir(path);
if (dir)
{
while ( (ent = readdir(dir)) != NULL )
{
if (strcmp(".", ent->d_name) == 0 || strcmp("..", ent->d_name) == 0)
continue;
if (ent->d_type == DT_DIR)
freeSlots -= 1;
}
}
closedir(dir);
}
return freeSlots;
}
//SD card
bool sdIsInserted()
{
//Find a better way to do this.
return true;
}
unsigned long long getSDCardSize()
{
if (sdIsInserted())
{
struct statvfs st;
if (statvfs("sd:/", &st) == 0)
return st.f_bsize * st.f_blocks;
}
return 0;
}
unsigned long long getSDCardFree()
{
if (sdIsInserted())
{
struct statvfs st;
if (statvfs("sd:/", &st) == 0)
return st.f_bsize * st.f_bavail;
}
return 0;
}
//internal storage
unsigned long long getDsiSize()
{
//The DSi has 256MB of internal storage. Some is unavailable and used by other things.
//An empty DSi reads 1024 open blocks
return 1024 * BYTES_PER_BLOCK;
}
unsigned long long getDsiFree()
{
u32 blockSize = getDsiClusterSize();
//Get free space by subtracting file sizes in nand folders
unsigned long long size = getDsiSize();
unsigned long long appSize = getDirSize(sdnandMode ? "sd:/title/00030004" : "nand:/title/00030004", blockSize);
//subtract, but don't go under 0
if (appSize > size)
{
size = 0;
}
else
{
size -= appSize;
}
unsigned long long realFree = getDsiRealFree();
return (realFree < size) ? realFree : size;
}
unsigned long long getDsiRealSize()
{
struct statvfs st;
if (statvfs(sdnandMode ? "sd:/" : "nand:/", &st) == 0)
return st.f_bsize * st.f_blocks;
return 0;
}
unsigned long long getDsiRealFree()
{
struct statvfs st;
if (statvfs(sdnandMode ? "sd:/" : "nand:/", &st) == 0)
return st.f_bsize * st.f_bavail;
return 0;
}
u32 getDsiClusterSize()
{
struct statvfs st;
if (statvfs(sdnandMode ? "sd:/" : "nand:/", &st) == 0)
return st.f_bsize;
return 0;
}

50
arm9/src/storage.h Normal file
View File

@ -0,0 +1,50 @@
#ifndef STORAGE_H
#define STORAGE_H
#include <nds/ndstypes.h>
#include <stdio.h>
#define BACKUP_PATH "sd:/_nds/ntm/backup"
#define BYTES_PER_BLOCK (1024*128)
//printing
void printBytes(unsigned long long bytes);
//progress bar
void printProgressBar(float percent);
void clearProgressBar();
//Files
bool fileExists(char const* path);
int copyFile(char const* src, char const* dst);
int copyFilePart(char const* src, u32 offset, u32 size, char const* dst);
unsigned long long getFileSize(FILE* f);
unsigned long long getFileSizePath(char const* path);
bool padFile(char const* path, int size);
//Directories
bool dirExists(char const* path);
bool copyDir(char const* src, char const* dst);
bool deleteDir(char const* path);
unsigned long long getDirSize(char const* path, u32 blockSize);
//home menu
int getMenuSlots();
int getMenuSlotsFree();
#define getMenuSlotsUsed() (getMenuSlots() - getMenuSlotsFree())
//SD card
bool sdIsInserted();
unsigned long long getSDCardSize();
unsigned long long getSDCardFree();
#define getSDCardUsedSpace() (getSDCardSize() - getSDCardFree())
//internal storage
unsigned long long getDsiSize();
unsigned long long getDsiFree();
unsigned long long getDsiRealSize();
unsigned long long getDsiRealFree();
u32 getDsiClusterSize();
#define getDsiUsed() (getDSIStorageSize() - getDSIStorageFree())
#endif

75
arm9/src/testmenu.c Normal file
View File

@ -0,0 +1,75 @@
#include "main.h"
#include "message.h"
#include "storage.h"
void testMenu()
{
//top screen
clearScreen(&topScreen);
iprintf("Storage Check Test\n\n");
//bottom screen
clearScreen(&bottomScreen);
unsigned int free = 0;
unsigned int size = 0;
//home menu slots
{
iprintf("Free Home Menu Slots:\n");
free = getMenuSlotsFree();
iprintf("\t%d / ", free);
size = getMenuSlots();
iprintf("%d\n", size);
}
//dsi menu
{
iprintf("\nFree DSi Menu Space:\n\t");
free = getDsiFree();
printBytes(free);
iprintf(" / ");
size = getDsiSize();
printBytes(size);
iprintf("\n");
iprintf("\t%d / %d blocks\n", free / BYTES_PER_BLOCK, size / BYTES_PER_BLOCK);
}
//nand
if (!sdnandMode)
{
iprintf("\nFree NAND Space:\n\t");
free = getDsiRealFree();
printBytes(free);
iprintf(" / ");
size = getDsiRealSize();
printBytes(size);
iprintf("\n");
}
//SD Card
{
iprintf("\nFree SD Space:\n\t");
unsigned long long sdfree = getSDCardFree();
printBytes(sdfree);
iprintf(" / ");
unsigned long long sdsize = getSDCardSize();
printBytes(sdsize);
iprintf("\n");
printf("\t%d / %d blocks\n", (unsigned int)(sdfree / BYTES_PER_BLOCK), (unsigned int)(sdsize / BYTES_PER_BLOCK));
}
//end
iprintf("\nBack - [B]\n");
keyWait(KEY_B);
}

523
arm9/src/titlemenu.c Normal file
View File

@ -0,0 +1,523 @@
#include "main.h"
#include "rom.h"
#include "menu.h"
#include "message.h"
#include "nand/nandio.h"
#include "storage.h"
#include <dirent.h>
enum {
TITLE_MENU_BACKUP,
TITLE_MENU_DELETE,
TITLE_MENU_READ_ONLY,
TITLE_MENU_BACK
};
static bool readOnly = false;
static void generateList(Menu* m);
static void printItem(Menu* m);
static int subMenu();
static void backup(Menu* m);
static bool delete(Menu* m);
static void toggleReadOnly(Menu* m);
void titleMenu()
{
Menu* m = newMenu();
setMenuHeader(m, "INSTALLED TITLES");
generateList(m);
//no titles
if (m->itemCount <= 0)
{
messageBox("No titles found.");
}
else
{
while (!programEnd)
{
swiWaitForVBlank();
scanKeys();
if (moveCursor(m))
{
if (m->changePage != 0)
generateList(m);
printMenu(m);
printItem(m);
}
if (keysDown() & KEY_B || m->itemCount <= 0)
break;
else if (keysDown() & KEY_A)
{
readOnly = FAT_getAttr(m->items[m->cursor].value) & ATTR_READONLY;
switch (subMenu())
{
case TITLE_MENU_BACKUP:
backup(m);
break;
case TITLE_MENU_DELETE:
{
if (delete(m))
{
resetMenu(m);
generateList(m);
}
}
break;
case TITLE_MENU_READ_ONLY:
toggleReadOnly(m);
break;
}
printMenu(m);
}
}
}
freeMenu(m);
}
static void generateList(Menu* m)
{
if (!m) return;
const int NUM_OF_DIRS = 4;
const char* dirs[] = {
"00030004",
"00030005",
"00030015",
"00030017"
};
const char* blacklist[4][6] = {
{ // 00030004
NULL //nothing blacklisted
},
{ // 00030005
"484e44", // DS Download Play
"484e45", // PictoChat
"484e49", // Nintendo DSi Camera
"484e4a", // Nintendo Zone
"484e4b", // Nintendo DSi Sound
NULL
},
{ // 00030015
"484e42", // System Settings
"484e46", // Nintendo DSi Shop
NULL
},
{ // 00030017
"484e41", // Launcher
NULL
}
};
//Reset menu
clearMenu(m);
m->page += sign(m->changePage);
m->changePage = 0;
bool done = false;
int count = 0; //used to skip to the right page
//search each category directory /title/XXXXXXXX
for (int i = 0; i < NUM_OF_DIRS && done == false; i++)
{
char* dirPath = (char*)malloc(strlen(dirs[i])+15);
sprintf(dirPath, "%s:/title/%s", sdnandMode ? "sd" : "nand", dirs[i]);
struct dirent* ent;
DIR* dir = opendir(dirPath);
if (dir)
{
while ( (ent = readdir(dir)) && done == false)
{
if (strcmp(".", ent->d_name) == 0 || strcmp("..", ent->d_name) == 0)
continue;
//blacklisted titles
if (!sdnandMode)
{
//if the region check somehow failed blacklist all-non DSiWare
if (region == 0 && i > 0) continue;
bool blacklisted = false;
for (int j = 0; blacklist[i][j] != NULL; j++)
{
char titleId[9];
sprintf(titleId, "%s%02x", blacklist[i][j], region);
if (strcmp(titleId, ent->d_name) == 0)
blacklisted = true;
// also blacklist specific all-region titles
if ((strcmp("484e4441", ent->d_name) == 0) || // Download Play
(strcmp("484e4541", ent->d_name) == 0) || // PictoChat
(strcmp("34544e41", ent->d_name) == 0)) // TwlNmenu
blacklisted = true;
}
if (blacklisted) continue;
}
if (ent->d_type == DT_DIR)
{
//scan content folder /title/XXXXXXXX/content
char* contentPath = (char*)malloc(strlen(dirPath) + strlen(ent->d_name) + 20);
sprintf(contentPath, "%s/%s/content", dirPath, ent->d_name);
struct dirent* subent;
DIR* subdir = opendir(contentPath);
if (subdir)
{
while ( (subent = readdir(subdir)) && done == false)
{
if (strcmp(".", subent->d_name) == 0 || strcmp("..", subent->d_name) == 0)
continue;
if (subent->d_type != DT_DIR)
{
//found .app file
if (strstr(subent->d_name, ".app") != NULL)
{
//current item is not on page
if (count < m->page * ITEMS_PER_PAGE)
count += 1;
else
{
if (m->itemCount >= ITEMS_PER_PAGE)
done = true;
else
{
//found requested title
char* path = (char*)malloc(strlen(contentPath) + strlen(subent->d_name) + 10);
sprintf(path, "%s/%s", contentPath, subent->d_name);
char title[128];
getGameTitlePath(path, title, false);
addMenuItem(m, title, path, 0);
free(path);
}
}
}
}
}
}
closedir(subdir);
free(contentPath);
}
}
}
closedir(dir);
free(dirPath);
}
sortMenuItems(m);
m->nextPage = done;
if (m->cursor >= m->itemCount)
m->cursor = m->itemCount - 1;
printItem(m);
printMenu(m);
}
static void printItem(Menu* m)
{
if (!m) return;
printRomInfo(m->items[m->cursor].value);
}
static int subMenu()
{
int result = -1;
Menu* m = newMenu();
addMenuItem(m, "Backup", NULL, 0);
addMenuItem(m, "Delete", NULL, 0);
addMenuItem(m, readOnly ? "Mark not read-only" : "Mark read-only", NULL, 0);
addMenuItem(m, "Back - [B]", NULL, 0);
printMenu(m);
while (!programEnd)
{
swiWaitForVBlank();
scanKeys();
if (moveCursor(m))
printMenu(m);
if (keysDown() & KEY_B)
break;
else if (keysDown() & KEY_A)
{
result = m->cursor;
break;
}
}
freeMenu(m);
return result;
}
static void backup(Menu* m)
{
char* fpath = m->items[m->cursor].value;
char *backname = NULL;
tDSiHeader* h = getRomHeader(fpath);
{
//make backup folder name
char label[13];
getRomLabel(h, label);
char gamecode[5];
getRomCode(h, gamecode);
backname = (char*)malloc(strlen(label) + strlen(gamecode) + 16);
sprintf(backname, "%s-%s", label, gamecode);
//make sure dir is unused
char* dstpath = (char*)malloc(strlen(BACKUP_PATH) + strlen(backname) + 32);
sprintf(dstpath, "%s/%s.nds", BACKUP_PATH, backname);
int try = 1;
while (access(dstpath, F_OK) == 0)
{
try += 1;
sprintf(backname, "%s-%s(%d)", label, gamecode, try);
sprintf(dstpath, "%s/%s.nds", BACKUP_PATH, backname);
}
free(dstpath);
}
bool choice = NO;
{
const char str[] = "Are you sure you want to backup\n";
char* msg = (char*)malloc(strlen(str) + strlen(backname) + 2);
sprintf(msg, "%s%s?", str, backname);
choice = choiceBox(msg);
free(msg);
}
if (choice == YES)
{
char srcpath[30];
sprintf(srcpath, "%s:/title/%08lx/%08lx", sdnandMode ? "sd" : "nand", h->tid_high, h->tid_low);
if (getSDCardFree() < getDirSize(srcpath, 0))
{
messageBox("Not enough space on SD.");
}
else
{
//create dirs
{
//create subdirectories
char backupPath[sizeof(BACKUP_PATH)];
strcpy(backupPath, BACKUP_PATH);
for (char *slash = strchr(backupPath, '/'); slash; slash = strchr(slash + 1, '/'))
{
char temp = *slash;
*slash = '\0';
mkdir(backupPath, 0777);
*slash = temp;
}
mkdir(backupPath, 0777); // sd:/_nds/ntm/backup
}
clearScreen(&bottomScreen);
char path[256], dstpath[256];
//tmd
sprintf(path, "%s/content/title.tmd", srcpath);
sprintf(dstpath, "%s/%s.tmd", BACKUP_PATH, backname);
if (access(path, F_OK) == 0)
{
//get app version
FILE *tmd = fopen(path, "rb");
if (tmd)
{
u8 appVersion[4];
fseek(tmd, 0x1E4, SEEK_SET);
fread(&appVersion, 1, 4, tmd);
fclose(tmd);
iprintf("%s -> \n%s...\n", path, dstpath);
copyFile(path, dstpath);
//app
sprintf(path, "%s/content/%02x%02x%02x%02x.app", srcpath, appVersion[0], appVersion[1], appVersion[2], appVersion[3]);
sprintf(dstpath, "%s/%s.nds", BACKUP_PATH, backname);
if (access(path, F_OK) == 0)
{
iprintf("%s -> \n%s...\n", path, dstpath);
copyFile(path, dstpath);
}
}
}
//public save
sprintf(path, "%s/data/public.sav", srcpath);
sprintf(dstpath, "%s/%s.pub", BACKUP_PATH, backname);
if (access(path, F_OK) == 0)
{
iprintf("%s -> \n%s...\n", path, dstpath);
copyFile(path, dstpath);
}
//private save
sprintf(path, "%s/data/private.sav", srcpath);
sprintf(dstpath, "%s/%s.prv", BACKUP_PATH, backname);
if (access(path, F_OK) == 0)
{
iprintf("%s -> \n%s...\n", path, dstpath);
copyFile(path, dstpath);
}
//banner save
sprintf(path, "%s/data/banner.sav", srcpath);
sprintf(dstpath, "%s/%s.bnr", BACKUP_PATH, backname);
if (access(path, F_OK) == 0)
{
iprintf("%s -> \n%s...\n", path, dstpath);
copyFile(path, dstpath);
}
messagePrint("\x1B[42m\nBackup finished.\x1B[47m");
}
}
free(h);
}
static bool delete(Menu* m)
{
if (!m) return false;
char* fpath = m->items[m->cursor].value;
bool result = false;
bool choice = NO;
{
//get app title
char title[128];
getGameTitlePath(m->items[m->cursor].value, title, false);
char str[] = "Are you sure you want to delete\n";
char* msg = (char*)malloc(strlen(str) + strlen(title) + 8);
sprintf(msg, "%s%s", str, title);
choice = choiceBox(msg);
free(msg);
}
if (choice == YES)
{
if (!fpath)
{
messageBox("Failed to delete title.\n");
}
else
{
char dirPath[64];
sprintf(dirPath, "%.*s", sdnandMode ? 27 : 29, fpath);
if (!dirExists(dirPath))
{
messageBox("Failed to delete title.\n");
}
else
{
if (!sdnandMode && !nandio_unlock_writing())
return false;
clearScreen(&bottomScreen);
if (deleteDir(dirPath))
{
result = true;
messagePrint("\nTitle deleted.\n");
}
else
{
messagePrint("\nTitle could not be deleted.\n");
}
if (!sdnandMode)
nandio_lock_writing();
}
}
}
return result;
}
static void toggleReadOnly(Menu* m)
{
if (!m) return;
tDSiHeader* h = getRomHeader(m->items[m->cursor].value);
char path[256];
char srcpath[30];
sprintf(srcpath, "%s:/title/%08lx/%08lx", sdnandMode ? "sd" : "nand", h->tid_high, h->tid_low);
if (!sdnandMode && !nandio_unlock_writing()) return;
//app
strcpy(path, m->items[m->cursor].value);
if (access(path, F_OK) == 0)
FAT_setAttr(path, FAT_getAttr(path) ^ ATTR_READONLY);
//tmd
sprintf(path, "%s/content/title.tmd", srcpath);
if (access(path, F_OK) == 0)
FAT_setAttr(path, FAT_getAttr(path) ^ ATTR_READONLY);
//public save
sprintf(path, "%s/data/public.sav", srcpath);
if (access(path, F_OK) == 0)
FAT_setAttr(path, FAT_getAttr(path) ^ ATTR_READONLY);
//private save
sprintf(path, "%s/data/private.sav", srcpath);
if (access(path, F_OK) == 0)
FAT_setAttr(path, FAT_getAttr(path) ^ ATTR_READONLY);
//banner save
sprintf(path, "%s/data/banner.sav", srcpath);
if (access(path, F_OK) == 0)
FAT_setAttr(path, FAT_getAttr(path) ^ ATTR_READONLY);
if (!sdnandMode)
nandio_lock_writing();
free(h);
messageBox("Title's read-only status\nsuccesfully toggled.");
}

BIN
icon.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 B