mirror of
https://github.com/wiiu-env/AutobootModule.git
synced 2024-11-01 01:25:05 +01:00
Compare commits
74 Commits
AutobootMo
...
main
Author | SHA1 | Date | |
---|---|---|---|
|
8a80fbab22 | ||
|
c02f4d7262 | ||
|
3505afad72 | ||
|
3d1995e41d | ||
|
0507dd772b | ||
|
023414fdda | ||
|
ad745814be | ||
|
8f52f74c13 | ||
|
d58ea5aad3 | ||
|
469e1d15f6 | ||
|
ca588cb0e5 | ||
|
0ce39e61be | ||
|
77afefaccb | ||
|
8be9598f1e | ||
|
7c987b5b9d | ||
|
8f495d213f | ||
|
713f2a3aed | ||
|
e6cfed9da8 | ||
|
d8b2f36cf0 | ||
|
083998faab | ||
|
b5c007d921 | ||
|
b2ae87ae1f | ||
|
67dfdd1fc8 | ||
|
4cc08d82e2 | ||
|
f80e83dff9 | ||
|
b245a71cdb | ||
|
688b834d2a | ||
|
bf8d1a974d | ||
|
f313152874 | ||
|
f1a240ddbc | ||
|
ed1e612602 | ||
|
011ab8bcf1 | ||
|
1830addd61 | ||
|
6f7061044a | ||
|
6954a7f9f5 | ||
|
4e821e4fa2 | ||
|
10161be0a2 | ||
|
77d5de813f | ||
|
803f2ca80b | ||
|
603478ccc6 | ||
|
1bff430b7d | ||
|
873521f884 | ||
|
2d023be0a2 | ||
|
8233db8868 | ||
|
a097915359 | ||
|
c33ef3d872 | ||
|
51eaca82b3 | ||
|
0e30c81e4a | ||
|
220dce54db | ||
|
ade0f3abf5 | ||
|
9d8d14d192 | ||
|
af68c340cf | ||
|
efe611712f | ||
|
a7b301ce2e | ||
|
96c02e85b3 | ||
|
6d0a7809dd | ||
|
7b586309a1 | ||
|
10ba1ff82c | ||
|
3f077c9993 | ||
|
daf8c8511a | ||
|
f324dad6fe | ||
|
1281dd3c51 | ||
|
fca58f95ce | ||
|
106c485fb1 | ||
|
c0f6e1744a | ||
|
d3a542d549 | ||
|
08ea530f8e | ||
|
c2228569bd | ||
|
0419bdd838 | ||
|
b6b1f99d85 | ||
|
f756a3c985 | ||
|
60fa230e19 | ||
|
c56897cc75 | ||
|
4512de73da |
@ -2,7 +2,8 @@
|
||||
BasedOnStyle: LLVM
|
||||
AccessModifierOffset: -4
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignConsecutiveAssignments: None
|
||||
AlignConsecutiveAssignments: Consecutive
|
||||
AlignConsecutiveMacros: AcrossEmptyLinesAndComments
|
||||
AlignOperands: Align
|
||||
AllowAllArgumentsOnNextLine: false
|
||||
AllowAllConstructorInitializersOnNextLine: false
|
||||
@ -56,7 +57,7 @@ SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 0
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInAngles: false
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInContainerLiterals: false
|
||||
|
10
.github/dependabot.yml
vendored
Normal file
10
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
42
.github/workflows/ci.yml
vendored
42
.github/workflows/ci.yml
vendored
@ -7,17 +7,24 @@ on:
|
||||
|
||||
jobs:
|
||||
clang-format:
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: clang-format
|
||||
run: |
|
||||
docker run --rm -v ${PWD}:/src wiiuenv/clang-format:13.0.0-2 -r ./source
|
||||
docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./source
|
||||
build-binary:
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-22.04
|
||||
needs: clang-format
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: create version.h
|
||||
run: |
|
||||
git_hash=$(git rev-parse --short "$GITHUB_SHA")
|
||||
cat <<EOF > ./source/version.h
|
||||
#pragma once
|
||||
#define AUTOBOOT_MODULE_VERSION_EXTRA " (nightly-$git_hash)"
|
||||
EOF
|
||||
- name: build binary
|
||||
run: |
|
||||
docker build . -t builder
|
||||
@ -28,7 +35,7 @@ jobs:
|
||||
path: "*.rpx"
|
||||
deploy-binary:
|
||||
needs: build-binary
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Get environment variables
|
||||
id: get_repository_name
|
||||
@ -41,25 +48,12 @@ jobs:
|
||||
- name: zip artifact
|
||||
run: zip -r ${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip *.rpx
|
||||
- name: Create Release
|
||||
id: create_release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
uses: "softprops/action-gh-release@v2"
|
||||
with:
|
||||
tag_name: ${{ env.REPOSITORY_NAME }}-${{ env.DATETIME }}
|
||||
release_name: Nightly-${{ env.REPOSITORY_NAME }}-${{ env.DATETIME }}
|
||||
draft: false
|
||||
prerelease: true
|
||||
body: |
|
||||
Not a stable release:
|
||||
${{ github.event.head_commit.message }}
|
||||
- name: Upload Release Asset
|
||||
id: upload-release-asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
|
||||
asset_path: ./${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip
|
||||
asset_name: ${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip
|
||||
asset_content_type: application/zip
|
||||
generate_release_notes: true
|
||||
name: Nightly-${{ env.REPOSITORY_NAME }}-${{ env.DATETIME }}
|
||||
files: |
|
||||
./${{ env.REPOSITORY_NAME }}_${{ env.DATETIME }}.zip
|
30
.github/workflows/pr.yml
vendored
30
.github/workflows/pr.yml
vendored
@ -4,17 +4,35 @@ on: [pull_request]
|
||||
|
||||
jobs:
|
||||
clang-format:
|
||||
runs-on: ubuntu-18.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: clang-format
|
||||
run: |
|
||||
docker run --rm -v ${PWD}:/src wiiuenv/clang-format:13.0.0-2 -r ./source
|
||||
build-binary:
|
||||
runs-on: ubuntu-18.04
|
||||
docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./source
|
||||
check-build-with-logging:
|
||||
runs-on: ubuntu-22.04
|
||||
needs: clang-format
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: build binary with logging
|
||||
run: |
|
||||
docker build . -t builder
|
||||
docker run --rm -v ${PWD}:/project builder make DEBUG=VERBOSE
|
||||
docker run --rm -v ${PWD}:/project builder make clean
|
||||
docker run --rm -v ${PWD}:/project builder make DEBUG=1
|
||||
build-binary:
|
||||
runs-on: ubuntu-22.04
|
||||
needs: clang-format
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: create version.h
|
||||
run: |
|
||||
git_hash=$(git rev-parse --short "${{ github.event.pull_request.head.sha }}")
|
||||
cat <<EOF > ./source/version.h
|
||||
#pragma once
|
||||
#define AUTOBOOT_MODULE_VERSION_EXTRA " (nightly-$git_hash)"
|
||||
EOF
|
||||
- name: build binary
|
||||
run: |
|
||||
docker build . -t builder
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -2,5 +2,7 @@
|
||||
*.rpx
|
||||
build/
|
||||
.idea/
|
||||
.vscode/
|
||||
cmake-build-debug/
|
||||
CMakeLists.txt
|
||||
*.zip
|
||||
|
@ -1,5 +1,6 @@
|
||||
FROM wiiuenv/devkitppc:20211229
|
||||
FROM ghcr.io/wiiu-env/devkitppc:20240704
|
||||
|
||||
COPY --from=wiiuenv/libiosuhax:20211008 /artifacts $DEVKITPRO
|
||||
COPY --from=ghcr.io/wiiu-env/libmocha:20230621 /artifacts $DEVKITPRO
|
||||
COPY --from=ghcr.io/wiiu-env/librpxloader:20240425 /artifacts $DEVKITPRO
|
||||
|
||||
WORKDIR project
|
674
LICENSE
Normal file
674
LICENSE
Normal file
@ -0,0 +1,674 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://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 <http://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:
|
||||
|
||||
{project} Copyright (C) {year} {fullname}
|
||||
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
|
||||
<http://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
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
19
Makefile
19
Makefile
@ -10,6 +10,8 @@ TOPDIR ?= $(CURDIR)
|
||||
|
||||
include $(DEVKITPRO)/wut/share/wut_rules
|
||||
|
||||
WUMS_ROOT := $(DEVKITPRO)/wums
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# TARGET is the name of the output
|
||||
# BUILD is the directory where object files & intermediate files will be placed
|
||||
@ -19,7 +21,7 @@ include $(DEVKITPRO)/wut/share/wut_rules
|
||||
#-------------------------------------------------------------------------------
|
||||
TARGET := 99_autoboot
|
||||
BUILD := build
|
||||
SOURCES := source
|
||||
SOURCES := source source/utils
|
||||
DATA := data
|
||||
INCLUDES := source include
|
||||
|
||||
@ -36,18 +38,23 @@ CXXFLAGS := $(CFLAGS) -std=c++20 -fno-rtti
|
||||
ASFLAGS := -g $(ARCH)
|
||||
LDFLAGS = -g $(ARCH) $(RPXSPECS) --entry=_start -Wl,-Map,$(notdir $*.map)
|
||||
|
||||
LIBS := -lfreetype -lpng -lbz2 -liosuhax -lwut -lz
|
||||
LIBS := -lrpxloader -lpng -lmocha -lwut -lz
|
||||
|
||||
ifeq ($(DEBUG),1)
|
||||
CXXFLAGS += -DDEBUG -g
|
||||
CCFLAGS += -DDEBUG -g
|
||||
CFLAGS += -DDEBUG -g
|
||||
endif
|
||||
|
||||
ifeq ($(DEBUG),VERBOSE)
|
||||
CXXFLAGS += -DDEBUG -DVERBOSE_DEBUG -g
|
||||
CFLAGS += -DDEBUG -DVERBOSE_DEBUG -g
|
||||
endif
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level
|
||||
# containing include and lib
|
||||
#-------------------------------------------------------------------------------
|
||||
LIBDIRS := $(PORTLIBS) $(WUT_ROOT) $(WUT_ROOT)/usr
|
||||
LIBDIRS := $(PORTLIBS) $(WUMS_ROOT) $(WUT_ROOT) $(WUT_ROOT)/usr
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# no real need to edit anything past this point unless you need to add additional
|
||||
@ -90,7 +97,7 @@ export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
|
||||
|
||||
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||
-I$(CURDIR)/$(BUILD) -I$(DEVKITPRO)/portlibs/ppc/include/freetype2
|
||||
-I$(CURDIR)/$(BUILD)
|
||||
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
|
||||
@ -100,7 +107,7 @@ export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
all: $(BUILD)
|
||||
|
||||
$(BUILD):
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
@$(shell [ ! -d $(BUILD) ] && mkdir -p $(BUILD))
|
||||
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
18
README.md
18
README.md
@ -3,9 +3,7 @@
|
||||
This is a bootmenu targetted to be loaded with the [EnvironmentLoader](https://github.com/wiiu-env/EnvironmentLoader). It allows you to boot into the Wii U Menu, Homebrew Channel, vWii System Menu or vWii Homebrew Channel.
|
||||
|
||||
## Usage
|
||||
Place the `99_autoboot` in the `[ENVIRONMENT]/modules/setup` folder and run the EnvironmentLoader.
|
||||
- Requires the [HBLInstallerWrapper](https://github.com/wiiu-env/HBLInstallerWrapper) in the `[ENVIRONMENT]/modules/setup` folder.
|
||||
- Requires the `homebrew_launcher.elf` in `sd:/wiiu/apps/homebrew_launcher/homebrew_launcher.elf`.
|
||||
Place the `99_autoboot.rpx` in the `[ENVIRONMENT]/modules/setup` folder and run the EnvironmentLoader.
|
||||
|
||||
Hold START (+) on the Gamepad while launching this Environment to force open the Autoboot menu.
|
||||
|
||||
@ -16,6 +14,17 @@ Press Y on the autoboot menu to set autobooting to this titles. To revert it, fo
|
||||
- Full support of Quick Boot Menu of the Gamepad when coldbooting
|
||||
- Set a autoboot title
|
||||
|
||||
## Buildflags
|
||||
|
||||
### Logging
|
||||
Building via `make` only logs errors (via OSReport). To enable logging via the [LoggingModule](https://github.com/wiiu-env/LoggingModule) set `DEBUG` to `1` or `VERBOSE`.
|
||||
|
||||
`make` Logs errors only (via OSReport).
|
||||
`make DEBUG=1` Enables information and error logging via [LoggingModule](https://github.com/wiiu-env/LoggingModule).
|
||||
`make DEBUG=VERBOSE` Enables verbose information and error logging via [LoggingModule](https://github.com/wiiu-env/LoggingModule).
|
||||
|
||||
If the [LoggingModule](https://github.com/wiiu-env/LoggingModule) is not present, it'll fallback to UDP (Port 4405) and [CafeOS](https://github.com/wiiu-env/USBSerialLoggingModule) logging.
|
||||
|
||||
## Building
|
||||
For building you just need [wut](https://github.com/devkitPro/wut/) installed, then use the `make` command.
|
||||
|
||||
@ -36,8 +45,9 @@ docker run -it --rm -v ${PWD}:/project autobootmodule-builder make clean
|
||||
|
||||
## Format the code via docker
|
||||
|
||||
`docker run --rm -v ${PWD}:/src wiiuenv/clang-format:13.0.0-2 -r ./source -i`
|
||||
`docker run --rm -v ${PWD}:/src ghcr.io/wiiu-env/clang-format:13.0.0-2 -r ./source -i`
|
||||
|
||||
## Credits
|
||||
- GaryOderNichts
|
||||
- Maschell
|
||||
- Crementif
|
||||
|
BIN
data/icon.png
BIN
data/icon.png
Binary file not shown.
Before Width: | Height: | Size: 12 KiB |
@ -1,40 +1,33 @@
|
||||
#include <codecvt>
|
||||
#include <locale>
|
||||
#include <malloc.h>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "ACTAccountInfo.h"
|
||||
#include "BootUtils.h"
|
||||
#include "DrawUtils.h"
|
||||
#include "ACTAccountInfo.h"
|
||||
#include "MenuUtils.h"
|
||||
#include "logger.h"
|
||||
|
||||
#include <coreinit/debug.h>
|
||||
#include <coreinit/screen.h>
|
||||
#include <gx2/state.h>
|
||||
#include <codecvt>
|
||||
#include <coreinit/filesystem_fsa.h>
|
||||
#include <locale>
|
||||
#include <malloc.h>
|
||||
#include <memory>
|
||||
#include <mocha/mocha.h>
|
||||
#include <nn/act.h>
|
||||
#include <nn/cmpt/cmpt.h>
|
||||
#include <padscore/kpad.h>
|
||||
#include <sndcore2/core.h>
|
||||
#include <string>
|
||||
#include <sysapp/launch.h>
|
||||
#include <sysapp/title.h>
|
||||
#include <vpad/input.h>
|
||||
|
||||
#include <iosuhax.h>
|
||||
#include <vector>
|
||||
|
||||
void handleAccountSelection();
|
||||
|
||||
void bootWiiUMenu() {
|
||||
nn::act::Initialize();
|
||||
nn::act::SlotNo slot = nn::act::GetSlotNo();
|
||||
nn::act::SlotNo slot = nn::act::GetSlotNo();
|
||||
nn::act::SlotNo defaultSlot = nn::act::GetDefaultAccount();
|
||||
nn::act::Finalize();
|
||||
|
||||
if (defaultSlot) {//normal menu boot
|
||||
if (defaultSlot) { //normal menu boot
|
||||
SYSLaunchMenu();
|
||||
} else {//show mii select
|
||||
} else { //show mii select
|
||||
_SYSLaunchMenuWithCheckingAccount(slot);
|
||||
}
|
||||
}
|
||||
@ -50,7 +43,7 @@ void handleAccountSelection() {
|
||||
nn::act::Initialize();
|
||||
nn::act::SlotNo defaultSlot = nn::act::GetDefaultAccount();
|
||||
|
||||
if (!defaultSlot) {// No default account is set.
|
||||
if (!defaultSlot) { // No default account is set.
|
||||
std::vector<std::shared_ptr<AccountInfo>> accountInfoList;
|
||||
for (int32_t i = 0; i < 13; i++) {
|
||||
if (!nn::act::IsSlotOccupied(i)) {
|
||||
@ -58,8 +51,8 @@ void handleAccountSelection() {
|
||||
}
|
||||
char16_t nameOut[nn::act::MiiNameSize];
|
||||
std::shared_ptr<AccountInfo> accountInfo = std::make_shared<AccountInfo>();
|
||||
accountInfo->slot = i;
|
||||
auto result = nn::act::GetMiiNameEx(reinterpret_cast<int16_t *>(nameOut), i);
|
||||
accountInfo->slot = i;
|
||||
auto result = nn::act::GetMiiNameEx(reinterpret_cast<int16_t *>(nameOut), i);
|
||||
if (result.IsSuccess()) {
|
||||
std::u16string source;
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert;
|
||||
@ -73,7 +66,7 @@ void handleAccountSelection() {
|
||||
}
|
||||
|
||||
uint32_t imageSize = 0;
|
||||
result = nn::act::GetMiiImageEx(&imageSize, accountInfo->miiImageBuffer, sizeof(accountInfo->miiImageBuffer), 0, i);
|
||||
result = nn::act::GetMiiImageEx(&imageSize, accountInfo->miiImageBuffer, sizeof(accountInfo->miiImageBuffer), 0, i);
|
||||
if (result.IsSuccess()) {
|
||||
accountInfo->miiImageSize = imageSize;
|
||||
}
|
||||
@ -81,6 +74,9 @@ void handleAccountSelection() {
|
||||
}
|
||||
|
||||
if (accountInfoList.size() > 0) {
|
||||
if (!AXIsInit()) {
|
||||
AXInit();
|
||||
}
|
||||
auto slot = handleAccountSelectScreen(accountInfoList);
|
||||
|
||||
DEBUG_FUNCTION_LINE("Load slot %d", slot);
|
||||
@ -90,7 +86,7 @@ void handleAccountSelection() {
|
||||
nn::act::Finalize();
|
||||
}
|
||||
|
||||
static void launchvWiiTitle(uint32_t titleId_low, uint32_t titleId_high) {
|
||||
static void launchvWiiTitle(uint64_t titleId) {
|
||||
// we need to init kpad for cmpt
|
||||
KPADInit();
|
||||
|
||||
@ -108,54 +104,55 @@ static void launchvWiiTitle(uint32_t titleId_low, uint32_t titleId_high) {
|
||||
|
||||
void *dataBuffer = memalign(0x40, dataSize);
|
||||
|
||||
if (titleId_low == 0 && titleId_high == 0) {
|
||||
if (titleId == 0) {
|
||||
CMPTLaunchMenu(dataBuffer, dataSize);
|
||||
} else {
|
||||
CMPTLaunchTitle(dataBuffer, dataSize, titleId_low, titleId_high);
|
||||
CMPTLaunchTitle(dataBuffer, dataSize, titleId);
|
||||
}
|
||||
|
||||
free(dataBuffer);
|
||||
}
|
||||
|
||||
void bootvWiiMenu() {
|
||||
launchvWiiTitle(0, 0);
|
||||
launchvWiiTitle(0);
|
||||
}
|
||||
|
||||
void bootHomebrewChannel() {
|
||||
uint64_t getVWiiHBLTitleId() {
|
||||
// fall back to booting the vWii system menu if anything fails
|
||||
uint64_t titleId = 0;
|
||||
|
||||
if (IOSUHAX_Open(nullptr) >= 0) {
|
||||
int fsaFd = IOSUHAX_FSA_Open();
|
||||
if (fsaFd >= 0) {
|
||||
FSAInit();
|
||||
auto client = FSAAddClient(nullptr);
|
||||
if (client > 0) {
|
||||
if (Mocha_UnlockFSClientEx(client) == MOCHA_RESULT_SUCCESS) {
|
||||
// mount the slccmpt
|
||||
if (IOSUHAX_FSA_Mount(fsaFd, "/dev/slccmpt01", "/vol/storage_slccmpt01", 2, nullptr, 0) >= 0) {
|
||||
fileStat_s stat;
|
||||
if (FSAMount(client, "/dev/slccmpt01", "/vol/storage_abm_slccmpt01", FSA_MOUNT_FLAG_GLOBAL_MOUNT, nullptr, 0) >= 0) {
|
||||
FSStat stat;
|
||||
|
||||
// test if the OHBC or HBC is installed
|
||||
if (IOSUHAX_FSA_GetStat(fsaFd, "/vol/storage_slccmpt01/title/00010001/4f484243/content/00000000.app", &stat) >= 0) {
|
||||
titleId = 0x000100014F484243L;// 'OHBC'
|
||||
} else if (IOSUHAX_FSA_GetStat(fsaFd, "/vol/storage_slccmpt01/title/00010001/4c554c5a/content/00000000.app", &stat) >= 0) {
|
||||
titleId = 0x000100014C554C5AL;// 'LULZ'
|
||||
if (FSAGetStat(client, "/vol/storage_abm_slccmpt01/title/00010001/4f484243/content/00000000.app", &stat) >= 0) {
|
||||
titleId = 0x000100014F484243L; // 'OHBC'
|
||||
} else if (FSAGetStat(client, "/vol/storage_abm_slccmpt01/title/00010001/4c554c5a/content/00000000.app", &stat) >= 0) {
|
||||
titleId = 0x000100014C554C5AL; // 'LULZ'
|
||||
} else {
|
||||
DEBUG_FUNCTION_LINE("Cannot find HBC, booting vWii System Menu");
|
||||
DEBUG_FUNCTION_LINE("Cannot find HBC");
|
||||
}
|
||||
|
||||
IOSUHAX_FSA_Unmount(fsaFd, "/vol/storage_slccmpt01", 2);
|
||||
FSAUnmount(client, "/vol/storage_abm_slccmpt01", FSA_UNMOUNT_FLAG_FORCE);
|
||||
} else {
|
||||
DEBUG_FUNCTION_LINE("Failed to mount SLCCMPT");
|
||||
DEBUG_FUNCTION_LINE_ERR("Failed to mount slccmpt01");
|
||||
}
|
||||
|
||||
IOSUHAX_FSA_Close(fsaFd);
|
||||
} else {
|
||||
DEBUG_FUNCTION_LINE("Failed to open FSA");
|
||||
DEBUG_FUNCTION_LINE_ERR("Failed to unlock FSClient");
|
||||
}
|
||||
|
||||
IOSUHAX_Close();
|
||||
FSADelClient(client);
|
||||
} else {
|
||||
DEBUG_FUNCTION_LINE("Failed to open IOSUHAX");
|
||||
DEBUG_FUNCTION_LINE_ERR("Failed to add FSAClient");
|
||||
}
|
||||
|
||||
DEBUG_FUNCTION_LINE("Launching vWii title %016llx", titleId);
|
||||
launchvWiiTitle((uint32_t) (titleId >> 32), (uint32_t) (titleId & 0xffffffff));
|
||||
return titleId;
|
||||
}
|
||||
|
||||
void bootHomebrewChannel() {
|
||||
uint64_t titleId = getVWiiHBLTitleId();
|
||||
DEBUG_FUNCTION_LINE("Launching vWii title %016llx", titleId);
|
||||
launchvWiiTitle(titleId);
|
||||
}
|
||||
|
@ -9,3 +9,5 @@ void bootHomebrewLauncher();
|
||||
void bootvWiiMenu();
|
||||
|
||||
void bootHomebrewChannel();
|
||||
|
||||
uint64_t getVWiiHBLTitleId();
|
@ -1,35 +1,52 @@
|
||||
#include "DrawUtils.h"
|
||||
#include <malloc.h>
|
||||
|
||||
#include <cmath>
|
||||
#include "MenuUtils.h"
|
||||
#include "logger.h"
|
||||
#include "utils.h"
|
||||
#include <coreinit/cache.h>
|
||||
#include <coreinit/memory.h>
|
||||
#include <coreinit/savedframe.h>
|
||||
#include <coreinit/screen.h>
|
||||
#include <ft2build.h>
|
||||
#include <cstdlib>
|
||||
#include <gx2/display.h>
|
||||
#include <gx2/state.h>
|
||||
#include <malloc.h>
|
||||
#include <png.h>
|
||||
#include FT_FREETYPE_H
|
||||
#include "MenuUtils.h"
|
||||
|
||||
// buffer width
|
||||
#define TV_WIDTH 0x500
|
||||
#define TV_WIDTH 0x500
|
||||
#define DRC_WIDTH 0x380
|
||||
|
||||
bool DrawUtils::isBackBuffer;
|
||||
|
||||
uint8_t *DrawUtils::tvBuffer = nullptr;
|
||||
uint32_t DrawUtils::tvSize = 0;
|
||||
uint8_t *DrawUtils::tvBuffer = nullptr;
|
||||
uint32_t DrawUtils::tvSize = 0;
|
||||
uint8_t *DrawUtils::drcBuffer = nullptr;
|
||||
uint32_t DrawUtils::drcSize = 0;
|
||||
uint32_t DrawUtils::drcSize = 0;
|
||||
static SFT pFont = {};
|
||||
|
||||
// Don't put those into the class or we have to include ft everywhere
|
||||
static FT_Library ft_lib = nullptr;
|
||||
static FT_Face ft_face = nullptr;
|
||||
static Color font_col = {0xFFFFFFFF};
|
||||
static Color font_col(0xFFFFFFFF);
|
||||
|
||||
void DrawUtils::ClearSavedFrameBuffers() {
|
||||
// If GX2 is running make sure to shut it down and free all existing memory in the saved-frame area.
|
||||
if (GX2GetMainCoreId() != -1) {
|
||||
GX2SetTVEnable(FALSE);
|
||||
GX2SetDRCEnable(FALSE);
|
||||
GX2Shutdown();
|
||||
}
|
||||
|
||||
__OSClearSavedFrame(OS_SAVED_FRAME_A, OS_SAVED_FRAME_SCREEN_TV);
|
||||
__OSClearSavedFrame(OS_SAVED_FRAME_A, OS_SAVED_FRAME_SCREEN_DRC);
|
||||
__OSClearSavedFrame(OS_SAVED_FRAME_B, OS_SAVED_FRAME_SCREEN_TV);
|
||||
__OSClearSavedFrame(OS_SAVED_FRAME_B, OS_SAVED_FRAME_SCREEN_DRC);
|
||||
}
|
||||
|
||||
void *DrawUtils::InitOSScreen() {
|
||||
ClearSavedFrameBuffers();
|
||||
|
||||
OSScreenInit();
|
||||
|
||||
uint32_t tvBufferSize = OSScreenGetBufferSizeEx(SCREEN_TV);
|
||||
uint32_t tvBufferSize = OSScreenGetBufferSizeEx(SCREEN_TV);
|
||||
uint32_t drcBufferSize = OSScreenGetBufferSizeEx(SCREEN_DRC);
|
||||
|
||||
auto *screenBuffer = (uint8_t *) memalign(0x100, tvBufferSize + drcBufferSize);
|
||||
@ -46,10 +63,10 @@ void *DrawUtils::InitOSScreen() {
|
||||
}
|
||||
|
||||
void DrawUtils::initBuffers(void *tvBuffer, uint32_t tvSize, void *drcBuffer, uint32_t drcSize) {
|
||||
DrawUtils::tvBuffer = (uint8_t *) tvBuffer;
|
||||
DrawUtils::tvSize = tvSize;
|
||||
DrawUtils::tvBuffer = (uint8_t *) tvBuffer;
|
||||
DrawUtils::tvSize = tvSize;
|
||||
DrawUtils::drcBuffer = (uint8_t *) drcBuffer;
|
||||
DrawUtils::drcSize = drcSize;
|
||||
DrawUtils::drcSize = drcSize;
|
||||
}
|
||||
|
||||
void DrawUtils::beginDraw() {
|
||||
@ -91,30 +108,37 @@ void DrawUtils::drawPixel(uint32_t x, uint32_t y, uint8_t r, uint8_t g, uint8_t
|
||||
i += drcSize / 2;
|
||||
}
|
||||
if (a == 0xFF) {
|
||||
drcBuffer[i] = r;
|
||||
drcBuffer[i] = r;
|
||||
drcBuffer[i + 1] = g;
|
||||
drcBuffer[i + 2] = b;
|
||||
} else {
|
||||
drcBuffer[i] = r * opacity + drcBuffer[i] * (1 - opacity);
|
||||
drcBuffer[i] = r * opacity + drcBuffer[i] * (1 - opacity);
|
||||
drcBuffer[i + 1] = g * opacity + drcBuffer[i + 1] * (1 - opacity);
|
||||
drcBuffer[i + 2] = b * opacity + drcBuffer[i + 2] * (1 - opacity);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t USED_TV_WIDTH = TV_WIDTH;
|
||||
float scale = 1.5f;
|
||||
if (DrawUtils::tvSize == 0x00FD2000) {
|
||||
USED_TV_WIDTH = 1920;
|
||||
scale = 2.25f;
|
||||
}
|
||||
|
||||
// scale and put pixel in the tv buffer
|
||||
for (uint32_t yy = (y * 1.5); yy < ((y * 1.5) + 1); yy++) {
|
||||
for (uint32_t xx = (x * 1.5); xx < ((x * 1.5) + 1); xx++) {
|
||||
uint32_t i = (xx + yy * TV_WIDTH) * 4;
|
||||
for (uint32_t yy = (y * scale); yy < ((y * scale) + (uint32_t) scale); yy++) {
|
||||
for (uint32_t xx = (x * scale); xx < ((x * scale) + (uint32_t) scale); xx++) {
|
||||
uint32_t i = (xx + yy * USED_TV_WIDTH) * 4;
|
||||
if (i + 3 < tvSize / 2) {
|
||||
if (isBackBuffer) {
|
||||
i += tvSize / 2;
|
||||
}
|
||||
if (a == 0xFF) {
|
||||
tvBuffer[i] = r;
|
||||
tvBuffer[i] = r;
|
||||
tvBuffer[i + 1] = g;
|
||||
tvBuffer[i + 2] = b;
|
||||
} else {
|
||||
tvBuffer[i] = r * opacity + tvBuffer[i] * (1 - opacity);
|
||||
tvBuffer[i] = r * opacity + tvBuffer[i] * (1 - opacity);
|
||||
tvBuffer[i + 1] = g * opacity + tvBuffer[i + 1] * (1 - opacity);
|
||||
tvBuffer[i + 2] = b * opacity + tvBuffer[i + 2] * (1 - opacity);
|
||||
}
|
||||
@ -145,8 +169,8 @@ void DrawUtils::drawBitmap(uint32_t x, uint32_t y, uint32_t target_width, uint32
|
||||
}
|
||||
|
||||
uint32_t dataPos = __builtin_bswap32(*(uint32_t *) &(data[0x0A]));
|
||||
uint32_t width = __builtin_bswap32(*(uint32_t *) &(data[0x12]));
|
||||
uint32_t height = __builtin_bswap32(*(uint32_t *) &(data[0x16]));
|
||||
uint32_t width = __builtin_bswap32(*(uint32_t *) &(data[0x12]));
|
||||
uint32_t height = __builtin_bswap32(*(uint32_t *) &(data[0x16]));
|
||||
|
||||
if (dataPos == 0) {
|
||||
dataPos = 54;
|
||||
@ -172,14 +196,14 @@ static void png_read_data(png_structp png_ptr, png_bytep outBytes, png_size_t by
|
||||
}
|
||||
|
||||
void DrawUtils::drawPNG(uint32_t x, uint32_t y, const uint8_t *data) {
|
||||
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
||||
if (png_ptr == NULL) {
|
||||
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
|
||||
if (png_ptr == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
png_infop info_ptr = png_create_info_struct(png_ptr);
|
||||
if (info_ptr == NULL) {
|
||||
png_destroy_read_struct(&png_ptr, NULL, NULL);
|
||||
if (info_ptr == nullptr) {
|
||||
png_destroy_read_struct(&png_ptr, nullptr, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -187,20 +211,20 @@ void DrawUtils::drawPNG(uint32_t x, uint32_t y, const uint8_t *data) {
|
||||
|
||||
png_read_info(png_ptr, info_ptr);
|
||||
|
||||
uint32_t width = 0;
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
int bitDepth = 0;
|
||||
int colorType = -1;
|
||||
uint32_t retval = png_get_IHDR(png_ptr, info_ptr, &width, &height, &bitDepth, &colorType, NULL, NULL, NULL);
|
||||
int bitDepth = 0;
|
||||
int colorType = -1;
|
||||
uint32_t retval = png_get_IHDR(png_ptr, info_ptr, &width, &height, &bitDepth, &colorType, nullptr, nullptr, nullptr);
|
||||
if (retval != 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t bytesPerRow = png_get_rowbytes(png_ptr, info_ptr);
|
||||
uint8_t *rowData = new uint8_t[bytesPerRow];
|
||||
auto *rowData = new uint8_t[bytesPerRow];
|
||||
|
||||
for (uint32_t yy = y; yy < y + height; yy++) {
|
||||
png_read_row(png_ptr, (png_bytep) rowData, NULL);
|
||||
png_read_row(png_ptr, (png_bytep) rowData, nullptr);
|
||||
|
||||
for (uint32_t xx = x; xx < x + width; xx++) {
|
||||
if (colorType == PNG_COLOR_TYPE_RGB_ALPHA) {
|
||||
@ -214,37 +238,52 @@ void DrawUtils::drawPNG(uint32_t x, uint32_t y, const uint8_t *data) {
|
||||
}
|
||||
|
||||
delete[] rowData;
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
||||
png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
|
||||
}
|
||||
|
||||
void DrawUtils::initFont() {
|
||||
void *font = NULL;
|
||||
bool DrawUtils::initFont() {
|
||||
void *font = nullptr;
|
||||
uint32_t size = 0;
|
||||
OSGetSharedData(OS_SHAREDDATATYPE_FONT_STANDARD, 0, &font, &size);
|
||||
|
||||
if (font && size) {
|
||||
FT_Init_FreeType(&ft_lib);
|
||||
FT_New_Memory_Face(ft_lib, (FT_Byte *) font, size, 0, &ft_face);
|
||||
pFont.xScale = 20;
|
||||
pFont.yScale = 20,
|
||||
pFont.flags = SFT_DOWNWARD_Y;
|
||||
pFont.font = sft_loadmem(font, size);
|
||||
if (!pFont.font) {
|
||||
return false;
|
||||
}
|
||||
OSMemoryBarrier();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void DrawUtils::deinitFont() {
|
||||
FT_Done_Face(ft_face);
|
||||
FT_Done_FreeType(ft_lib);
|
||||
sft_freefont(pFont.font);
|
||||
pFont.font = nullptr;
|
||||
pFont = {};
|
||||
}
|
||||
|
||||
void DrawUtils::setFontSize(uint32_t size) {
|
||||
FT_Set_Pixel_Sizes(ft_face, 0, size);
|
||||
pFont.xScale = size;
|
||||
pFont.yScale = size;
|
||||
SFT_LMetrics metrics;
|
||||
sft_lmetrics(&pFont, &metrics);
|
||||
}
|
||||
|
||||
void DrawUtils::setFontColor(Color col) {
|
||||
font_col = col;
|
||||
}
|
||||
|
||||
static void draw_freetype_bitmap(FT_Bitmap *bitmap, FT_Int x, FT_Int y) {
|
||||
FT_Int i, j, p, q;
|
||||
FT_Int x_max = x + bitmap->width;
|
||||
FT_Int y_max = y + bitmap->rows;
|
||||
static void draw_freetype_bitmap(SFT_Image *bmp, int32_t x, int32_t y) {
|
||||
int32_t i, j, p, q;
|
||||
|
||||
int32_t x_max = x + bmp->width;
|
||||
int32_t y_max = y + bmp->height;
|
||||
|
||||
auto *src = (uint8_t *) bmp->pixels;
|
||||
|
||||
for (i = x, p = 0; i < x_max; i++, p++) {
|
||||
for (j = y, q = 0; j < y_max; j++, q++) {
|
||||
@ -252,14 +291,14 @@ static void draw_freetype_bitmap(FT_Bitmap *bitmap, FT_Int x, FT_Int y) {
|
||||
continue;
|
||||
}
|
||||
|
||||
float opacity = bitmap->buffer[q * bitmap->pitch + p] / 255.0f;
|
||||
float opacity = src[q * bmp->width + p] / 255.0f;
|
||||
DrawUtils::drawPixel(i, j, font_col.r, font_col.g, font_col.b, font_col.a * opacity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DrawUtils::print(uint32_t x, uint32_t y, const char *string, bool alignRight) {
|
||||
wchar_t *buffer = new wchar_t[strlen(string) + 1];
|
||||
auto *buffer = new wchar_t[strlen(string) + 1];
|
||||
|
||||
size_t num = mbstowcs(buffer, string, strlen(string));
|
||||
if (num > 0) {
|
||||
@ -275,32 +314,64 @@ void DrawUtils::print(uint32_t x, uint32_t y, const char *string, bool alignRigh
|
||||
}
|
||||
|
||||
void DrawUtils::print(uint32_t x, uint32_t y, const wchar_t *string, bool alignRight) {
|
||||
FT_GlyphSlot slot = ft_face->glyph;
|
||||
FT_Vector pen = {(int) x, (int) y};
|
||||
auto penX = (int32_t) x;
|
||||
auto penY = (int32_t) y;
|
||||
|
||||
if (alignRight) {
|
||||
pen.x -= getTextWidth(string);
|
||||
penX -= getTextWidth(string);
|
||||
}
|
||||
|
||||
uint16_t textureWidth = 0, textureHeight = 0;
|
||||
for (; *string; string++) {
|
||||
uint32_t charcode = *string;
|
||||
SFT_Glyph gid; // unsigned long gid;
|
||||
if (sft_lookup(&pFont, *string, &gid) >= 0) {
|
||||
SFT_GMetrics mtx;
|
||||
if (sft_gmetrics(&pFont, gid, &mtx) < 0) {
|
||||
DEBUG_FUNCTION_LINE_ERR("Failed to get glyph metrics");
|
||||
return;
|
||||
}
|
||||
|
||||
if (charcode == '\n') {
|
||||
pen.y += ft_face->size->metrics.height >> 6;
|
||||
pen.x = x;
|
||||
continue;
|
||||
if (*string == '\n') {
|
||||
penY += mtx.minHeight;
|
||||
penX = x;
|
||||
continue;
|
||||
}
|
||||
|
||||
textureWidth = (mtx.minWidth + 3) & ~3;
|
||||
textureHeight = mtx.minHeight;
|
||||
|
||||
SFT_Image img = {
|
||||
.pixels = nullptr,
|
||||
.width = textureWidth,
|
||||
.height = textureHeight,
|
||||
};
|
||||
|
||||
if (textureWidth == 0) {
|
||||
textureWidth = 4;
|
||||
}
|
||||
if (textureHeight == 0) {
|
||||
textureHeight = 4;
|
||||
}
|
||||
|
||||
auto buffer = make_unique_nothrow<uint8_t[]>((uint32_t) (img.width * img.height));
|
||||
if (!buffer) {
|
||||
DEBUG_FUNCTION_LINE_ERR("Failed to allocate memory for glyph");
|
||||
return;
|
||||
}
|
||||
img.pixels = buffer.get();
|
||||
if (sft_render(&pFont, gid, img) < 0) {
|
||||
DEBUG_FUNCTION_LINE_ERR("Failed to render glyph");
|
||||
return;
|
||||
} else {
|
||||
draw_freetype_bitmap(&img, (int32_t) (penX + mtx.leftSideBearing), (int32_t) (penY + mtx.yOffset));
|
||||
penX += (int32_t) mtx.advanceWidth;
|
||||
}
|
||||
}
|
||||
|
||||
FT_Load_Glyph(ft_face, FT_Get_Char_Index(ft_face, charcode), FT_LOAD_DEFAULT);
|
||||
FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL);
|
||||
|
||||
draw_freetype_bitmap(&slot->bitmap, pen.x + slot->bitmap_left, pen.y - slot->bitmap_top);
|
||||
pen.x += slot->advance.x >> 6;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t DrawUtils::getTextWidth(const char *string) {
|
||||
wchar_t *buffer = new wchar_t[strlen(string) + 1];
|
||||
auto *buffer = new wchar_t[strlen(string) + 1];
|
||||
|
||||
size_t num = mbstowcs(buffer, string, strlen(string));
|
||||
if (num > 0) {
|
||||
@ -318,14 +389,18 @@ uint32_t DrawUtils::getTextWidth(const char *string) {
|
||||
}
|
||||
|
||||
uint32_t DrawUtils::getTextWidth(const wchar_t *string) {
|
||||
FT_GlyphSlot slot = ft_face->glyph;
|
||||
uint32_t width = 0;
|
||||
|
||||
for (; *string; string++) {
|
||||
FT_Load_Glyph(ft_face, FT_Get_Char_Index(ft_face, *string), FT_LOAD_BITMAP_METRICS_ONLY);
|
||||
|
||||
width += slot->advance.x >> 6;
|
||||
SFT_Glyph gid; // unsigned long gid;
|
||||
if (sft_lookup(&pFont, *string, &gid) >= 0) {
|
||||
SFT_GMetrics mtx;
|
||||
if (sft_gmetrics(&pFont, gid, &mtx) < 0) {
|
||||
DEBUG_FUNCTION_LINE_ERR("bad glyph metrics");
|
||||
}
|
||||
width += (int32_t) mtx.advanceWidth;
|
||||
}
|
||||
}
|
||||
|
||||
return width;
|
||||
return (uint32_t) width;
|
||||
}
|
||||
|
@ -1,13 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "schrift.h"
|
||||
#include <cstdint>
|
||||
|
||||
// visible screen sizes
|
||||
#define SCREEN_WIDTH 854
|
||||
#define SCREEN_WIDTH 854
|
||||
#define SCREEN_HEIGHT 480
|
||||
|
||||
union Color {
|
||||
Color(uint32_t color) {
|
||||
explicit Color(uint32_t color) {
|
||||
this->color = color;
|
||||
}
|
||||
|
||||
@ -18,7 +19,7 @@ union Color {
|
||||
this->a = a;
|
||||
}
|
||||
|
||||
uint32_t color;
|
||||
uint32_t color{};
|
||||
struct {
|
||||
uint8_t r;
|
||||
uint8_t g;
|
||||
@ -29,6 +30,8 @@ union Color {
|
||||
|
||||
class DrawUtils {
|
||||
public:
|
||||
static void ClearSavedFrameBuffers();
|
||||
|
||||
static void *InitOSScreen();
|
||||
|
||||
static void initBuffers(void *tvBuffer, uint32_t tvSize, void *drcBuffer, uint32_t drcSize);
|
||||
@ -51,7 +54,7 @@ public:
|
||||
|
||||
static void drawPNG(uint32_t x, uint32_t y, const uint8_t *data);
|
||||
|
||||
static void initFont();
|
||||
static bool initFont();
|
||||
|
||||
static void deinitFont();
|
||||
|
||||
|
140
source/InputUtils.cpp
Normal file
140
source/InputUtils.cpp
Normal file
@ -0,0 +1,140 @@
|
||||
#include "InputUtils.h"
|
||||
#include <coreinit/thread.h>
|
||||
#include <padscore/kpad.h>
|
||||
#include <padscore/wpad.h>
|
||||
#include <vpad/input.h>
|
||||
|
||||
uint32_t remapWiiMoteButtons(uint32_t buttons) {
|
||||
uint32_t convButtons = 0;
|
||||
|
||||
if (buttons & WPAD_BUTTON_LEFT)
|
||||
convButtons |= VPAD_BUTTON_LEFT;
|
||||
|
||||
if (buttons & WPAD_BUTTON_RIGHT)
|
||||
convButtons |= VPAD_BUTTON_RIGHT;
|
||||
|
||||
if (buttons & WPAD_BUTTON_DOWN)
|
||||
convButtons |= VPAD_BUTTON_DOWN;
|
||||
|
||||
if (buttons & WPAD_BUTTON_UP)
|
||||
convButtons |= VPAD_BUTTON_UP;
|
||||
|
||||
if (buttons & WPAD_BUTTON_PLUS)
|
||||
convButtons |= VPAD_BUTTON_PLUS;
|
||||
|
||||
if (buttons & WPAD_BUTTON_2)
|
||||
convButtons |= VPAD_BUTTON_Y;
|
||||
|
||||
if (buttons & WPAD_BUTTON_1)
|
||||
convButtons |= VPAD_BUTTON_X;
|
||||
|
||||
if (buttons & WPAD_BUTTON_B)
|
||||
convButtons |= VPAD_BUTTON_B;
|
||||
|
||||
if (buttons & WPAD_BUTTON_A)
|
||||
convButtons |= VPAD_BUTTON_A;
|
||||
|
||||
if (buttons & WPAD_BUTTON_MINUS)
|
||||
convButtons |= VPAD_BUTTON_MINUS;
|
||||
|
||||
if (buttons & WPAD_BUTTON_HOME)
|
||||
convButtons |= VPAD_BUTTON_HOME;
|
||||
|
||||
return convButtons;
|
||||
}
|
||||
|
||||
uint32_t remapClassicButtons(uint32_t buttons) {
|
||||
uint32_t convButtons = 0;
|
||||
|
||||
if (buttons & WPAD_CLASSIC_BUTTON_LEFT)
|
||||
convButtons |= VPAD_BUTTON_LEFT;
|
||||
|
||||
if (buttons & WPAD_CLASSIC_BUTTON_RIGHT)
|
||||
convButtons |= VPAD_BUTTON_RIGHT;
|
||||
|
||||
if (buttons & WPAD_CLASSIC_BUTTON_DOWN)
|
||||
convButtons |= VPAD_BUTTON_DOWN;
|
||||
|
||||
if (buttons & WPAD_CLASSIC_BUTTON_UP)
|
||||
convButtons |= VPAD_BUTTON_UP;
|
||||
|
||||
if (buttons & WPAD_CLASSIC_BUTTON_PLUS)
|
||||
convButtons |= VPAD_BUTTON_PLUS;
|
||||
|
||||
if (buttons & WPAD_CLASSIC_BUTTON_X)
|
||||
convButtons |= VPAD_BUTTON_X;
|
||||
|
||||
if (buttons & WPAD_CLASSIC_BUTTON_Y)
|
||||
convButtons |= VPAD_BUTTON_Y;
|
||||
|
||||
if (buttons & WPAD_CLASSIC_BUTTON_B)
|
||||
convButtons |= VPAD_BUTTON_B;
|
||||
|
||||
if (buttons & WPAD_CLASSIC_BUTTON_A)
|
||||
convButtons |= VPAD_BUTTON_A;
|
||||
|
||||
if (buttons & WPAD_CLASSIC_BUTTON_MINUS)
|
||||
convButtons |= VPAD_BUTTON_MINUS;
|
||||
|
||||
if (buttons & WPAD_CLASSIC_BUTTON_HOME)
|
||||
convButtons |= VPAD_BUTTON_HOME;
|
||||
|
||||
if (buttons & WPAD_CLASSIC_BUTTON_ZR)
|
||||
convButtons |= VPAD_BUTTON_ZR;
|
||||
|
||||
if (buttons & WPAD_CLASSIC_BUTTON_ZL)
|
||||
convButtons |= VPAD_BUTTON_ZL;
|
||||
|
||||
if (buttons & WPAD_CLASSIC_BUTTON_R)
|
||||
convButtons |= VPAD_BUTTON_R;
|
||||
|
||||
if (buttons & WPAD_CLASSIC_BUTTON_L)
|
||||
convButtons |= VPAD_BUTTON_L;
|
||||
|
||||
return convButtons;
|
||||
}
|
||||
|
||||
InputUtils::InputData InputUtils::getControllerInput() {
|
||||
InputData inputData = {};
|
||||
VPADStatus vpadStatus = {};
|
||||
VPADReadError vpadError = VPAD_READ_UNINITIALIZED;
|
||||
int maxAttempts = 100;
|
||||
do {
|
||||
if (VPADRead(VPAD_CHAN_0, &vpadStatus, 1, &vpadError) > 0 && vpadError == VPAD_READ_SUCCESS) {
|
||||
inputData.trigger = vpadStatus.trigger;
|
||||
inputData.hold = vpadStatus.hold;
|
||||
inputData.release = vpadStatus.release;
|
||||
} else {
|
||||
OSSleepTicks(OSMillisecondsToTicks(1));
|
||||
}
|
||||
} while (--maxAttempts > 0 && vpadError == VPAD_READ_NO_SAMPLES);
|
||||
|
||||
KPADStatus kpadStatus = {};
|
||||
KPADError kpadError = KPAD_ERROR_UNINITIALIZED;
|
||||
for (int32_t i = 0; i < 4; i++) {
|
||||
if (KPADReadEx((KPADChan) i, &kpadStatus, 1, &kpadError) > 0) {
|
||||
if (kpadError == KPAD_ERROR_OK && kpadStatus.extensionType != 0xFF) {
|
||||
if (kpadStatus.extensionType == WPAD_EXT_CORE || kpadStatus.extensionType == WPAD_EXT_NUNCHUK) {
|
||||
inputData.trigger |= remapWiiMoteButtons(kpadStatus.trigger);
|
||||
inputData.hold |= remapWiiMoteButtons(kpadStatus.hold);
|
||||
inputData.release |= remapWiiMoteButtons(kpadStatus.release);
|
||||
} else {
|
||||
inputData.trigger |= remapClassicButtons(kpadStatus.classic.trigger);
|
||||
inputData.hold |= remapClassicButtons(kpadStatus.classic.hold);
|
||||
inputData.release |= remapClassicButtons(kpadStatus.classic.release);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return inputData;
|
||||
}
|
||||
|
||||
void InputUtils::Init() {
|
||||
KPADInit();
|
||||
WPADEnableURCC(1);
|
||||
}
|
||||
|
||||
void InputUtils::DeInit() {
|
||||
KPADShutdown();
|
||||
}
|
17
source/InputUtils.h
Normal file
17
source/InputUtils.h
Normal file
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <vpad/input.h>
|
||||
|
||||
class InputUtils {
|
||||
public:
|
||||
typedef struct InputData {
|
||||
uint32_t trigger = 0;
|
||||
uint32_t hold = 0;
|
||||
uint32_t release = 0;
|
||||
} InputData;
|
||||
|
||||
static void Init();
|
||||
static void DeInit();
|
||||
|
||||
static InputData getControllerInput();
|
||||
};
|
@ -1,28 +1,27 @@
|
||||
#include "MenuUtils.h"
|
||||
#include "ACTAccountInfo.h"
|
||||
#include "DrawUtils.h"
|
||||
#include <cstdint>
|
||||
#include "InputUtils.h"
|
||||
#include "PairUtils.h"
|
||||
#include "logger.h"
|
||||
#include "main.h"
|
||||
#include "utils.h"
|
||||
#include "version.h"
|
||||
#include <coreinit/debug.h>
|
||||
#include <coreinit/filesystem_fsa.h>
|
||||
#include <coreinit/screen.h>
|
||||
#include <coreinit/thread.h>
|
||||
#include <cstring>
|
||||
#include <gx2/state.h>
|
||||
#include <malloc.h>
|
||||
#include <memory>
|
||||
#include <mocha/mocha.h>
|
||||
#include <sndcore2/core.h>
|
||||
#include <string>
|
||||
#include <sysapp/title.h>
|
||||
#include <vector>
|
||||
|
||||
#include <coreinit/debug.h>
|
||||
#include <coreinit/screen.h>
|
||||
#include <gx2/state.h>
|
||||
#include <memory>
|
||||
#include <nn/act/client_cpp.h>
|
||||
#include <vpad/input.h>
|
||||
|
||||
#include "ACTAccountInfo.h"
|
||||
#include "icon_png.h"
|
||||
#include "logger.h"
|
||||
|
||||
const char *menu_options[] = {
|
||||
"Wii U Menu",
|
||||
"Homebrew Launcher",
|
||||
"vWii System Menu",
|
||||
"vWii Homebrew Channel",
|
||||
};
|
||||
#define AUTOBOOT_MODULE_VERSION "v0.3.1"
|
||||
|
||||
const char *autoboot_config_strings[] = {
|
||||
"wiiu_menu",
|
||||
@ -31,15 +30,6 @@ const char *autoboot_config_strings[] = {
|
||||
"vwii_homebrew_channel",
|
||||
};
|
||||
|
||||
template<typename... Args>
|
||||
std::string string_format(const std::string &format, Args... args) {
|
||||
int size_s = std::snprintf(nullptr, 0, format.c_str(), args...) + 1;// Extra space for '\0'
|
||||
auto size = static_cast<size_t>(size_s);
|
||||
auto buf = std::make_unique<char[]>(size);
|
||||
std::snprintf(buf.get(), size, format.c_str(), args...);
|
||||
return std::string(buf.get(), buf.get() + size - 1);// We don't want the '\0' inside
|
||||
}
|
||||
|
||||
int32_t readAutobootOption(std::string &configPath) {
|
||||
FILE *f = fopen(configPath.c_str(), "r");
|
||||
if (f) {
|
||||
@ -69,83 +59,139 @@ void writeAutobootOption(std::string &configPath, int32_t autobootOption) {
|
||||
}
|
||||
}
|
||||
|
||||
int32_t handleMenuScreen(std::string &configPath, int32_t autobootOptionInput) {
|
||||
auto screenBuffer = DrawUtils::InitOSScreen();
|
||||
if (!screenBuffer) {
|
||||
OSFatal("Failed to alloc memory for screen");
|
||||
void drawMenuScreen(const std::map<uint32_t, std::string> &menu, uint32_t selectedIndex, uint32_t autobootIndex, bool updatesBlocked) {
|
||||
DrawUtils::beginDraw();
|
||||
DrawUtils::clear(COLOR_BACKGROUND);
|
||||
|
||||
// draw buttons
|
||||
uint32_t index = 8 + 24 + 8 + 4;
|
||||
for (uint32_t i = 0; i < menu.size(); i++) {
|
||||
if (i == (uint32_t) selectedIndex) {
|
||||
DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16 * 2, 44, 4, COLOR_BORDER_HIGHLIGHTED);
|
||||
} else {
|
||||
DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16 * 2, 44, 2, (i == (uint32_t) autobootIndex) ? COLOR_AUTOBOOT : COLOR_BORDER);
|
||||
}
|
||||
|
||||
std::string curName = std::next(menu.begin(), i)->second;
|
||||
|
||||
DrawUtils::setFontSize(24);
|
||||
DrawUtils::setFontColor((i == (uint32_t) autobootIndex) ? COLOR_AUTOBOOT : COLOR_TEXT);
|
||||
DrawUtils::print(16 * 2, index + 8 + 24, curName.c_str());
|
||||
index += 42 + 8;
|
||||
}
|
||||
|
||||
uint32_t tvBufferSize = OSScreenGetBufferSizeEx(SCREEN_TV);
|
||||
DrawUtils::setFontColor(COLOR_TEXT);
|
||||
|
||||
// draw top bar
|
||||
DrawUtils::setFontSize(24);
|
||||
DrawUtils::print(16, 6 + 24, "Boot Selector");
|
||||
DrawUtils::drawRectFilled(8, 8 + 24 + 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_WHITE);
|
||||
DrawUtils::setFontSize(16);
|
||||
DrawUtils::print(SCREEN_WIDTH - 16, 6 + 24, AUTOBOOT_MODULE_VERSION AUTOBOOT_MODULE_VERSION_EXTRA, true);
|
||||
|
||||
// draw bottom bar
|
||||
DrawUtils::drawRectFilled(8, SCREEN_HEIGHT - 24 - 8 - 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_WHITE);
|
||||
DrawUtils::setFontSize(18);
|
||||
DrawUtils::print(16, SCREEN_HEIGHT - 8, "\ue07d Navigate ");
|
||||
DrawUtils::print(SCREEN_WIDTH - 16, SCREEN_HEIGHT - 8, "\ue000 Choose", true);
|
||||
const char *autobootHints = "\ue002/\ue046 Clear Autoboot / \ue003/\ue045 Select Autoboot";
|
||||
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(autobootHints) / 2, SCREEN_HEIGHT - 8, autobootHints, true);
|
||||
|
||||
if (updatesBlocked) {
|
||||
DrawUtils::setFontSize(10);
|
||||
DrawUtils::print(SCREEN_WIDTH - 16, SCREEN_HEIGHT - 24 - 8 - 4 - 10, "Updates blocked! Hold \ue045 + \ue046 to restore Update folder", true);
|
||||
} else {
|
||||
DrawUtils::setFontSize(10);
|
||||
DrawUtils::print(SCREEN_WIDTH - 16, SCREEN_HEIGHT - 24 - 8 - 4 - 10, "Updates not blocked! Hold \ue045 + \ue046 to delete Update folder", true);
|
||||
}
|
||||
|
||||
DrawUtils::endDraw();
|
||||
}
|
||||
|
||||
int32_t handleMenuScreen(std::string &configPath, int32_t autobootOptionInput, const std::map<uint32_t, std::string> &menu) {
|
||||
auto screenBuffer = DrawUtils::InitOSScreen();
|
||||
if (!screenBuffer) {
|
||||
OSFatal("AutobootModule: Failed to alloc memory for screen");
|
||||
}
|
||||
|
||||
uint32_t tvBufferSize = OSScreenGetBufferSizeEx(SCREEN_TV);
|
||||
uint32_t drcBufferSize = OSScreenGetBufferSizeEx(SCREEN_DRC);
|
||||
|
||||
DrawUtils::initBuffers(screenBuffer, tvBufferSize, (void *) ((uint32_t) screenBuffer + tvBufferSize), drcBufferSize);
|
||||
DrawUtils::initFont();
|
||||
if (!DrawUtils::initFont()) {
|
||||
OSFatal("AutobootModule: Failed to init font");
|
||||
}
|
||||
|
||||
uint32_t selected = autobootOptionInput > 0 ? autobootOptionInput : 0;
|
||||
int autoboot = autobootOptionInput;
|
||||
bool redraw = true;
|
||||
while (true) {
|
||||
VPADStatus vpad{};
|
||||
VPADRead(VPAD_CHAN_0, &vpad, 1, nullptr);
|
||||
int32_t selectedIndex = autobootOptionInput > 0 ? autobootOptionInput : 0;
|
||||
int autobootIndex = autobootOptionInput;
|
||||
|
||||
if (vpad.trigger & VPAD_BUTTON_UP) {
|
||||
if (selected > 0) {
|
||||
selected--;
|
||||
redraw = true;
|
||||
}
|
||||
} else if (vpad.trigger & VPAD_BUTTON_DOWN) {
|
||||
if (selected < sizeof(menu_options) / sizeof(char *) - 1) {
|
||||
selected++;
|
||||
redraw = true;
|
||||
}
|
||||
} else if (vpad.trigger & VPAD_BUTTON_A) {
|
||||
// "Convert" id to index
|
||||
int32_t offset = 0;
|
||||
for (auto &item : menu) {
|
||||
if ((uint32_t) selectedIndex == item.first) {
|
||||
selectedIndex = offset;
|
||||
break;
|
||||
} else if (vpad.trigger & VPAD_BUTTON_X) {
|
||||
autoboot = -1;
|
||||
redraw = true;
|
||||
} else if (vpad.trigger & VPAD_BUTTON_Y) {
|
||||
autoboot = selected;
|
||||
redraw = true;
|
||||
}
|
||||
offset++;
|
||||
}
|
||||
if (autobootIndex > 0) {
|
||||
offset = 0;
|
||||
for (auto &item : menu) {
|
||||
if ((uint32_t) autobootIndex == item.first) {
|
||||
autobootIndex = offset;
|
||||
break;
|
||||
}
|
||||
offset++;
|
||||
}
|
||||
}
|
||||
|
||||
if (redraw) {
|
||||
DrawUtils::beginDraw();
|
||||
DrawUtils::clear(COLOR_BACKGROUND);
|
||||
{
|
||||
PairMenu pairMenu;
|
||||
|
||||
// draw buttons
|
||||
uint32_t index = 8 + 24 + 8 + 4;
|
||||
for (uint32_t i = 0; i < sizeof(menu_options) / sizeof(char *); i++) {
|
||||
if (i == selected) {
|
||||
DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16 * 2, 44, 4, COLOR_BORDER_HIGHLIGHTED);
|
||||
} else {
|
||||
DrawUtils::drawRect(16, index, SCREEN_WIDTH - 16 * 2, 44, 2, (i == (uint32_t) autoboot) ? COLOR_AUTOBOOT : COLOR_BORDER);
|
||||
int32_t holdUpdateBlockedForFrames = 0;
|
||||
while (true) {
|
||||
if (pairMenu.ProcessPairScreen()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
InputUtils::InputData input = InputUtils::getControllerInput();
|
||||
|
||||
if (input.trigger & VPAD_BUTTON_UP) {
|
||||
selectedIndex--;
|
||||
|
||||
if (selectedIndex < 0) {
|
||||
selectedIndex = 0;
|
||||
}
|
||||
|
||||
DrawUtils::setFontSize(24);
|
||||
DrawUtils::setFontColor((i == (uint32_t) autoboot) ? COLOR_AUTOBOOT : COLOR_TEXT);
|
||||
DrawUtils::print(16 * 2, index + 8 + 24, menu_options[i]);
|
||||
index += 42 + 8;
|
||||
} else if (input.trigger & VPAD_BUTTON_DOWN) {
|
||||
if (!menu.empty()) {
|
||||
selectedIndex++;
|
||||
|
||||
if ((uint32_t) selectedIndex >= menu.size()) {
|
||||
selectedIndex = menu.size() - 1;
|
||||
}
|
||||
}
|
||||
} else if (input.trigger & VPAD_BUTTON_A) {
|
||||
break;
|
||||
} else if (input.trigger & (VPAD_BUTTON_X | VPAD_BUTTON_MINUS)) {
|
||||
autobootIndex = -1;
|
||||
} else if (input.trigger & (VPAD_BUTTON_Y | VPAD_BUTTON_PLUS)) {
|
||||
autobootIndex = selectedIndex;
|
||||
} else if ((input.hold & (VPAD_BUTTON_PLUS | VPAD_BUTTON_MINUS)) == (VPAD_BUTTON_PLUS | VPAD_BUTTON_MINUS)) {
|
||||
if (holdUpdateBlockedForFrames++ > 50) {
|
||||
if (gUpdatesBlocked) {
|
||||
gUpdatesBlocked = !RestoreMLCUpdateDirectory();
|
||||
} else {
|
||||
gUpdatesBlocked = DeleteMLCUpdateDirectory();
|
||||
}
|
||||
holdUpdateBlockedForFrames = 0;
|
||||
}
|
||||
} else {
|
||||
holdUpdateBlockedForFrames = 0;
|
||||
}
|
||||
|
||||
DrawUtils::setFontColor(COLOR_TEXT);
|
||||
|
||||
// draw top bar
|
||||
DrawUtils::setFontSize(24);
|
||||
DrawUtils::drawPNG(16, 2, icon_png);
|
||||
DrawUtils::print(64 + 2, 6 + 24, "Tiramisu Boot Selector");
|
||||
DrawUtils::drawRectFilled(8, 8 + 24 + 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_WHITE);
|
||||
|
||||
// draw bottom bar
|
||||
DrawUtils::drawRectFilled(8, SCREEN_HEIGHT - 24 - 8 - 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_WHITE);
|
||||
DrawUtils::setFontSize(18);
|
||||
DrawUtils::print(16, SCREEN_HEIGHT - 8, "\ue07d Navigate ");
|
||||
DrawUtils::print(SCREEN_WIDTH - 16, SCREEN_HEIGHT - 8, "\ue000 Choose", true);
|
||||
const char *autobootHints = "\ue002 Clear Autoboot / \ue003 Select Autoboot";
|
||||
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(autobootHints) / 2, SCREEN_HEIGHT - 8, autobootHints, true);
|
||||
|
||||
DrawUtils::endDraw();
|
||||
|
||||
redraw = false;
|
||||
drawMenuScreen(menu, selectedIndex, autobootIndex, gUpdatesBlocked);
|
||||
}
|
||||
}
|
||||
|
||||
@ -160,6 +206,16 @@ int32_t handleMenuScreen(std::string &configPath, int32_t autobootOptionInput) {
|
||||
|
||||
free(screenBuffer);
|
||||
|
||||
int32_t selected = -1;
|
||||
int32_t autoboot = -1;
|
||||
// convert index to key
|
||||
if (selectedIndex >= 0 && (uint32_t) selectedIndex < menu.size()) {
|
||||
selected = std::next(menu.begin(), selectedIndex)->first;
|
||||
}
|
||||
if (autobootIndex >= 0 && (uint32_t) autobootIndex < menu.size()) {
|
||||
autoboot = std::next(menu.begin(), autobootIndex)->first;
|
||||
}
|
||||
|
||||
if (autoboot != autobootOptionInput) {
|
||||
writeAutobootOption(configPath, autoboot);
|
||||
}
|
||||
@ -167,62 +223,64 @@ int32_t handleMenuScreen(std::string &configPath, int32_t autobootOptionInput) {
|
||||
return selected;
|
||||
}
|
||||
|
||||
|
||||
nn::act::SlotNo handleAccountSelectScreen(const std::vector<std::shared_ptr<AccountInfo>> &data) {
|
||||
auto screenBuffer = DrawUtils::InitOSScreen();
|
||||
if (!screenBuffer) {
|
||||
OSFatal("Failed to alloc memory for screen");
|
||||
OSFatal("AutobootModule: Failed to alloc memory for screen");
|
||||
}
|
||||
|
||||
uint32_t tvBufferSize = OSScreenGetBufferSizeEx(SCREEN_TV);
|
||||
uint32_t tvBufferSize = OSScreenGetBufferSizeEx(SCREEN_TV);
|
||||
uint32_t drcBufferSize = OSScreenGetBufferSizeEx(SCREEN_DRC);
|
||||
|
||||
DrawUtils::initBuffers(screenBuffer, tvBufferSize, (void *) ((uint32_t) screenBuffer + tvBufferSize), drcBufferSize);
|
||||
DrawUtils::initFont();
|
||||
if (!DrawUtils::initFont()) {
|
||||
OSFatal("AutobootModule: Failed to init font");
|
||||
}
|
||||
|
||||
int32_t selected = 0;
|
||||
bool redraw = true;
|
||||
while (true) {
|
||||
VPADStatus vpad{};
|
||||
VPADRead(VPAD_CHAN_0, &vpad, 1, nullptr);
|
||||
|
||||
if (vpad.trigger & VPAD_BUTTON_UP) {
|
||||
if (selected > 0) {
|
||||
selected--;
|
||||
redraw = true;
|
||||
{
|
||||
PairMenu pairMenu;
|
||||
while (true) {
|
||||
if (pairMenu.ProcessPairScreen()) {
|
||||
continue;
|
||||
}
|
||||
} else if (vpad.trigger & VPAD_BUTTON_DOWN) {
|
||||
if (selected < (int32_t) data.size() - 1) {
|
||||
selected++;
|
||||
redraw = true;
|
||||
}
|
||||
} else if (vpad.trigger & VPAD_BUTTON_A) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (redraw) {
|
||||
InputUtils::InputData input = InputUtils::getControllerInput();
|
||||
if (input.trigger & VPAD_BUTTON_UP) {
|
||||
if (selected > 0) {
|
||||
selected--;
|
||||
}
|
||||
} else if (input.trigger & VPAD_BUTTON_DOWN) {
|
||||
if (selected < (int32_t) data.size() - 1) {
|
||||
selected++;
|
||||
}
|
||||
} else if (input.trigger & VPAD_BUTTON_A) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
DrawUtils::beginDraw();
|
||||
DrawUtils::clear(COLOR_BACKGROUND);
|
||||
|
||||
// draw buttons
|
||||
uint32_t index = 8 + 24 + 8 + 4;
|
||||
int32_t start = (selected / 5) * 5;
|
||||
int32_t end = (start + 5) < (int32_t) data.size() ? (start + 5) : data.size();
|
||||
int32_t start = (selected / 5) * 5;
|
||||
int32_t end = (start + 5) < (int32_t) data.size() ? (start + 5) : data.size();
|
||||
for (int i = start; i < end; i++) {
|
||||
auto &val = data[i];
|
||||
if (val->miiImageSize > 0) {
|
||||
// Draw Mii
|
||||
auto width = 128;
|
||||
auto height = 128;
|
||||
auto width = 128;
|
||||
auto height = 128;
|
||||
auto target_height = 64u;
|
||||
auto target_width = 64u;
|
||||
auto xOffset = 20;
|
||||
auto yOffset = index;
|
||||
auto target_width = 64u;
|
||||
auto xOffset = 20;
|
||||
auto yOffset = index;
|
||||
for (uint32_t y = 0; y < target_height; y++) {
|
||||
for (uint32_t x = 0; x < target_width; x++) {
|
||||
uint32_t col = (((x) *width / target_width) + ((target_height - y - 1) * height / target_height) * width) * 4;
|
||||
uint32_t col = (((x) *width / target_width) + ((target_height - y - 1) * height / target_height) * width) * 4;
|
||||
uint32_t colVal = ((uint32_t *) &val->miiImageBuffer[col + 1])[0];
|
||||
if (colVal == 0x00808080) {// Remove the green background.
|
||||
if (colVal == 0x00808080) { // Remove the green background.
|
||||
DrawUtils::drawPixel(x + xOffset, y + yOffset, COLOR_BACKGROUND.r, COLOR_BACKGROUND.g, COLOR_BACKGROUND.b, COLOR_BACKGROUND.a);
|
||||
} else {
|
||||
DrawUtils::drawPixel(x + xOffset, y + yOffset, val->miiImageBuffer[col + 1], val->miiImageBuffer[col + 2], val->miiImageBuffer[col + 3], val->miiImageBuffer[col]);
|
||||
@ -249,7 +307,7 @@ nn::act::SlotNo handleAccountSelectScreen(const std::vector<std::shared_ptr<Acco
|
||||
// draw top bar
|
||||
DrawUtils::setFontSize(24);
|
||||
DrawUtils::print(16, 6 + 24, "Select your Account");
|
||||
auto curPage = (selected / 5) + 1;
|
||||
auto curPage = (selected / 5) + 1;
|
||||
auto totalPages = data.size() % 5 == 0 ? data.size() / 5 : data.size() / 5 + 1;
|
||||
DrawUtils::print(SCREEN_WIDTH - 50, 6 + 24, string_format("%d/%d", curPage, totalPages).c_str());
|
||||
DrawUtils::drawRectFilled(8, 8 + 24 + 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_WHITE);
|
||||
@ -271,8 +329,6 @@ nn::act::SlotNo handleAccountSelectScreen(const std::vector<std::shared_ptr<Acco
|
||||
}
|
||||
|
||||
DrawUtils::endDraw();
|
||||
|
||||
redraw = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -287,7 +343,7 @@ nn::act::SlotNo handleAccountSelectScreen(const std::vector<std::shared_ptr<Acco
|
||||
|
||||
free(screenBuffer);
|
||||
|
||||
auto i = 0;
|
||||
auto i = 0;
|
||||
nn::act::SlotNo resultSlot = 0;
|
||||
for (auto const &val : data) {
|
||||
if (i == selected) {
|
||||
@ -298,3 +354,230 @@ nn::act::SlotNo handleAccountSelectScreen(const std::vector<std::shared_ptr<Acco
|
||||
|
||||
return resultSlot;
|
||||
}
|
||||
|
||||
void drawUpdateWarningScreen() {
|
||||
DrawUtils::beginDraw();
|
||||
DrawUtils::clear(COLOR_BACKGROUND_WARN);
|
||||
|
||||
DrawUtils::setFontColor(COLOR_WARNING);
|
||||
|
||||
// draw top bar
|
||||
DrawUtils::setFontSize(48);
|
||||
const char *title = "! Warning !";
|
||||
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(title) / 2, 48 + 8, title, true);
|
||||
DrawUtils::drawRectFilled(8, 48 + 8 + 16, SCREEN_WIDTH - 8 * 2, 3, COLOR_WHITE);
|
||||
|
||||
DrawUtils::setFontSize(24);
|
||||
|
||||
const char *message = "The update folder currently exists and is not a file.";
|
||||
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(message) / 2, SCREEN_HEIGHT / 2 - 48, message, true);
|
||||
message = "Your system might not be blocking updates properly!";
|
||||
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(message) / 2, SCREEN_HEIGHT / 2 - 24, message, true);
|
||||
|
||||
message = "Press \ue002 to block the updates! This can be reverted in the Boot Selector.";
|
||||
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(message) / 2, SCREEN_HEIGHT / 2 + 24, message, true);
|
||||
|
||||
message = "See https://wiiu.hacks.guide/#/block-updates for more information.";
|
||||
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(message) / 2, SCREEN_HEIGHT / 2 + 64 + 24, message, true);
|
||||
|
||||
DrawUtils::setFontSize(16);
|
||||
|
||||
message = "Press the SYNC Button on the Wii U console to connect a controller or GamePad.";
|
||||
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(message) / 2, SCREEN_HEIGHT - 48, message, true);
|
||||
|
||||
// draw bottom bar
|
||||
DrawUtils::drawRectFilled(8, SCREEN_HEIGHT - 24 - 8 - 4, SCREEN_WIDTH - 8 * 2, 3, COLOR_WHITE);
|
||||
DrawUtils::setFontSize(18);
|
||||
const char *exitHints = "\ue000 Continue without blocking / \ue001 Don't show this again";
|
||||
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(exitHints) / 2, SCREEN_HEIGHT - 8, exitHints, true);
|
||||
|
||||
DrawUtils::endDraw();
|
||||
}
|
||||
|
||||
void handleUpdateWarningScreen() {
|
||||
FILE *f = fopen(UPDATE_SKIP_PATH, "r");
|
||||
if (f) {
|
||||
DEBUG_FUNCTION_LINE("Skipping update warning screen");
|
||||
fclose(f);
|
||||
return;
|
||||
}
|
||||
|
||||
auto screenBuffer = DrawUtils::InitOSScreen();
|
||||
if (!screenBuffer) {
|
||||
OSFatal("AutobootModule: Failed to alloc memory for screen");
|
||||
}
|
||||
|
||||
uint32_t tvBufferSize = OSScreenGetBufferSizeEx(SCREEN_TV);
|
||||
uint32_t drcBufferSize = OSScreenGetBufferSizeEx(SCREEN_DRC);
|
||||
|
||||
DrawUtils::initBuffers(screenBuffer, tvBufferSize, (void *) ((uint32_t) screenBuffer + tvBufferSize), drcBufferSize);
|
||||
if (!DrawUtils::initFont()) {
|
||||
OSFatal("AutobootModule: Failed to init font");
|
||||
}
|
||||
|
||||
{
|
||||
PairMenu pairMenu;
|
||||
|
||||
while (true) {
|
||||
if (pairMenu.ProcessPairScreen()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
drawUpdateWarningScreen();
|
||||
|
||||
InputUtils::InputData input = InputUtils::getControllerInput();
|
||||
if (input.trigger & VPAD_BUTTON_A) {
|
||||
break;
|
||||
} else if (input.trigger & VPAD_BUTTON_X) {
|
||||
gUpdatesBlocked = DeleteMLCUpdateDirectory();
|
||||
break;
|
||||
} else if (input.trigger & VPAD_BUTTON_B) {
|
||||
f = fopen(UPDATE_SKIP_PATH, "w");
|
||||
if (f) {
|
||||
// It's **really** important to have this text on the stack.
|
||||
// If it's read from the .rodata section the fwrite will softlock the console because the OSEffectiveToPhysical returns NULL for
|
||||
// everything between 0x00800000 - 0x01000000 at this stage.
|
||||
const char text[] = "If this file exists, the Autoboot Module will not warn you about not blocking updates";
|
||||
fputs(text, f);
|
||||
fclose(f);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DrawUtils::beginDraw();
|
||||
DrawUtils::clear(COLOR_BLACK);
|
||||
DrawUtils::endDraw();
|
||||
|
||||
DrawUtils::deinitFont();
|
||||
|
||||
GX2Init(nullptr);
|
||||
|
||||
free(screenBuffer);
|
||||
}
|
||||
|
||||
void drawDiscInsert(bool wrongDiscInserted) {
|
||||
DrawUtils::beginDraw();
|
||||
DrawUtils::clear(COLOR_BACKGROUND);
|
||||
DrawUtils::setFontColor(COLOR_TEXT);
|
||||
|
||||
DrawUtils::setFontSize(48);
|
||||
|
||||
if (wrongDiscInserted) {
|
||||
const char *title = "The disc inserted into the console";
|
||||
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(title) / 2, 40 + 48 + 8, title, true);
|
||||
title = "is for a different software title.";
|
||||
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(title) / 2, 40 + 2 * 48 + 8, title, true);
|
||||
title = "Please change the disc.";
|
||||
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(title) / 2, 40 + 4 * 48 + 8, title, true);
|
||||
} else {
|
||||
const char *title = "Please insert a disc.";
|
||||
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(title) / 2, 40 + 48 + 8, title, true);
|
||||
}
|
||||
|
||||
DrawUtils::setFontSize(18);
|
||||
const char *exitHints = "\ue000 Launch Wii U Menu";
|
||||
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(exitHints) / 2, SCREEN_HEIGHT - 8, exitHints, true);
|
||||
|
||||
DrawUtils::endDraw();
|
||||
}
|
||||
|
||||
bool handleDiscInsertScreen(uint64_t expectedTitleId, uint64_t *titleIdToLaunch) {
|
||||
if (titleIdToLaunch == nullptr) {
|
||||
DEBUG_FUNCTION_LINE_ERR("titleIdToLaunch is NULL");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SYSCheckTitleExists(expectedTitleId)) {
|
||||
*titleIdToLaunch = expectedTitleId;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t titleIdOfDisc = 0;
|
||||
bool discInserted;
|
||||
|
||||
uint32_t attempt = 0;
|
||||
while (!GetTitleIdOfDisc(&titleIdOfDisc, &discInserted)) {
|
||||
if (++attempt > 20) {
|
||||
break;
|
||||
}
|
||||
OSSleepTicks(OSMillisecondsToTicks(100));
|
||||
}
|
||||
|
||||
bool wrongDiscInserted = discInserted && (titleIdOfDisc != expectedTitleId);
|
||||
|
||||
if (discInserted && !wrongDiscInserted) {
|
||||
*titleIdToLaunch = expectedTitleId;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!AXIsInit()) {
|
||||
AXInit();
|
||||
}
|
||||
|
||||
bool result;
|
||||
auto screenBuffer = DrawUtils::InitOSScreen();
|
||||
if (!screenBuffer) {
|
||||
OSFatal("AutobootModule: Failed to alloc memory for screen");
|
||||
}
|
||||
|
||||
uint32_t tvBufferSize = OSScreenGetBufferSizeEx(SCREEN_TV);
|
||||
uint32_t drcBufferSize = OSScreenGetBufferSizeEx(SCREEN_DRC);
|
||||
|
||||
DrawUtils::initBuffers(screenBuffer, tvBufferSize, (void *) ((uint32_t) screenBuffer + tvBufferSize), drcBufferSize);
|
||||
if (!DrawUtils::initFont()) {
|
||||
OSFatal("AutobootModule: Failed to init font");
|
||||
}
|
||||
DrawUtils::beginDraw();
|
||||
DrawUtils::clear(COLOR_BACKGROUND);
|
||||
DrawUtils::endDraw();
|
||||
|
||||
// When an unexpected disc was inserted we need to eject it first.
|
||||
bool allowDisc = !wrongDiscInserted;
|
||||
{
|
||||
PairMenu pairMenu;
|
||||
|
||||
while (true) {
|
||||
if (pairMenu.ProcessPairScreen()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
drawDiscInsert(wrongDiscInserted);
|
||||
|
||||
InputUtils::InputData input = InputUtils::getControllerInput();
|
||||
if (input.trigger & VPAD_BUTTON_A) {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (GetTitleIdOfDisc(&titleIdOfDisc, &discInserted)) {
|
||||
if (discInserted) {
|
||||
if (!allowDisc) {
|
||||
continue;
|
||||
}
|
||||
*titleIdToLaunch = titleIdOfDisc;
|
||||
DEBUG_FUNCTION_LINE("Disc inserted! %016llX", titleIdOfDisc);
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
allowDisc = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DrawUtils::beginDraw();
|
||||
DrawUtils::clear(COLOR_BLACK);
|
||||
DrawUtils::endDraw();
|
||||
|
||||
DrawUtils::deinitFont();
|
||||
|
||||
// Call GX2Init to shut down OSScreen
|
||||
GX2Init(nullptr);
|
||||
|
||||
free(screenBuffer);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -2,21 +2,24 @@
|
||||
|
||||
#include "ACTAccountInfo.h"
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <nn/act.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// clang-format off
|
||||
#define COLOR_WHITE Color(0xffffffff)
|
||||
#define COLOR_BLACK Color(0, 0, 0, 255)
|
||||
#define COLOR_BACKGROUND COLOR_BLACK
|
||||
#define COLOR_TEXT COLOR_WHITE
|
||||
#define COLOR_TEXT2 Color(0xB3ffffff)
|
||||
#define COLOR_AUTOBOOT Color(0xaeea00ff)
|
||||
#define COLOR_BORDER Color(204, 204, 204, 255)
|
||||
#define COLOR_BORDER_HIGHLIGHTED Color(0x3478e4ff)
|
||||
// clang-format on
|
||||
#define UPDATE_SKIP_PATH "fs:/vol/external01/wiiu/environments/skipUpdateWarn"
|
||||
|
||||
#define COLOR_WHITE Color(0xffffffff)
|
||||
#define COLOR_BLACK Color(0, 0, 0, 255)
|
||||
#define COLOR_BACKGROUND COLOR_BLACK
|
||||
#define COLOR_BACKGROUND_WARN Color(255, 251, 4, 255)
|
||||
#define COLOR_TEXT COLOR_WHITE
|
||||
#define COLOR_WARNING COLOR_BLACK
|
||||
#define COLOR_TEXT2 Color(0xB3ffffff)
|
||||
#define COLOR_AUTOBOOT Color(0xaeea00ff)
|
||||
#define COLOR_BORDER Color(204, 204, 204, 255)
|
||||
#define COLOR_BORDER_HIGHLIGHTED Color(0x3478e4ff)
|
||||
|
||||
enum {
|
||||
BOOT_OPTION_WII_U_MENU,
|
||||
@ -29,6 +32,10 @@ int32_t readAutobootOption(std::string &configPath);
|
||||
|
||||
void writeAutobootOption(std::string &configPath, int32_t autobootOption);
|
||||
|
||||
int32_t handleMenuScreen(std::string &configPath, int32_t autobootOptionInput);
|
||||
int32_t handleMenuScreen(std::string &configPath, int32_t autobootOptionInput, const std::map<uint32_t, std::string> &menu);
|
||||
|
||||
nn::act::SlotNo handleAccountSelectScreen(const std::vector<std::shared_ptr<AccountInfo>> &data);
|
||||
|
||||
void handleUpdateWarningScreen();
|
||||
|
||||
bool handleDiscInsertScreen(uint64_t expectedTitleId, uint64_t *titleIdToLaunch);
|
||||
|
239
source/PairUtils.cpp
Normal file
239
source/PairUtils.cpp
Normal file
@ -0,0 +1,239 @@
|
||||
#include "PairUtils.h"
|
||||
#include "DrawUtils.h"
|
||||
#include "InputUtils.h"
|
||||
#include "logger.h"
|
||||
#include "utils.h"
|
||||
#include <coreinit/cache.h>
|
||||
#include <coreinit/thread.h>
|
||||
#include <malloc.h>
|
||||
#include <nn/ccr/sys.h>
|
||||
#include <padscore/kpad.h>
|
||||
#include <padscore/wpad.h>
|
||||
#include <vpad/input.h>
|
||||
|
||||
void PairMenu::drawPairKPADScreen() const {
|
||||
DrawUtils::beginDraw();
|
||||
DrawUtils::clear(COLOR_BACKGROUND);
|
||||
|
||||
DrawUtils::setFontColor(COLOR_TEXT);
|
||||
|
||||
DrawUtils::setFontSize(26);
|
||||
|
||||
std::string textLine1 = "Press the SYNC Button on the controller you want to pair.";
|
||||
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(textLine1.c_str()) / 2, 40, textLine1.c_str(), true);
|
||||
|
||||
|
||||
WPADExtensionType ext{};
|
||||
for (int i = 0; i < 4; i++) {
|
||||
bool isConnected = WPADProbe((WPADChan) i, &ext) == 0;
|
||||
std::string textLine = string_format("Slot %d: ", i + 1);
|
||||
if (isConnected) {
|
||||
textLine += ext == WPAD_EXT_PRO_CONTROLLER ? "Pro Controller" : "Wiimote";
|
||||
} else {
|
||||
textLine += "No controller";
|
||||
}
|
||||
|
||||
DrawUtils::print(300, 140 + (i * 30), textLine.c_str());
|
||||
}
|
||||
|
||||
DrawUtils::setFontSize(26);
|
||||
|
||||
std::string gamepadSyncText1 = "If you are pairing a Wii U GamePad, press the SYNC Button";
|
||||
std::string gamepadSyncText2 = "on your Wii U console one more time";
|
||||
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(gamepadSyncText1.c_str()) / 2, SCREEN_HEIGHT - 100, gamepadSyncText1.c_str(), true);
|
||||
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(gamepadSyncText2.c_str()) / 2, SCREEN_HEIGHT - 70, gamepadSyncText2.c_str(), true);
|
||||
|
||||
DrawUtils::setFontSize(16);
|
||||
|
||||
const char *exitHints = "Press \ue001 to return";
|
||||
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(exitHints) / 2, SCREEN_HEIGHT - 8, exitHints, true);
|
||||
|
||||
DrawUtils::endDraw();
|
||||
}
|
||||
|
||||
void PairMenu::drawPairScreen() const {
|
||||
DrawUtils::beginDraw();
|
||||
DrawUtils::clear(COLOR_BACKGROUND);
|
||||
|
||||
DrawUtils::setFontColor(COLOR_TEXT);
|
||||
|
||||
// Convert the pin to symbols and set the text
|
||||
static char pinSymbols[][4] = {
|
||||
"\u2660",
|
||||
"\u2665",
|
||||
"\u2666",
|
||||
"\u2663"};
|
||||
|
||||
uint32_t pincode = mGamePadPincode;
|
||||
|
||||
std::string pin = std::string(pinSymbols[(pincode / 1000) % 10]) +
|
||||
pinSymbols[(pincode / 100) % 10] +
|
||||
pinSymbols[(pincode / 10) % 10] +
|
||||
pinSymbols[pincode % 10];
|
||||
|
||||
std::string textLine1 = "Press the SYNC Button on the Wii U GamePad,";
|
||||
std::string textLine2 = "and enter the four symbols shown below.";
|
||||
|
||||
DrawUtils::setFontSize(26);
|
||||
|
||||
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(textLine1.c_str()) / 2, 60, textLine1.c_str(), true);
|
||||
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(textLine2.c_str()) / 2, 100, textLine2.c_str(), true);
|
||||
|
||||
DrawUtils::setFontSize(100);
|
||||
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(pin.c_str()) / 2, (SCREEN_HEIGHT / 2) + 40, pin.c_str(), true);
|
||||
|
||||
DrawUtils::setFontSize(20);
|
||||
|
||||
std::string textLine3 = string_format("(%d seconds remaining) ", mGamePadSyncTimeout - (uint32_t) (OSTicksToSeconds(OSGetTime() - mSyncGamePadStartTime)));
|
||||
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(textLine3.c_str()) / 2, SCREEN_HEIGHT - 80, textLine3.c_str(), true);
|
||||
|
||||
DrawUtils::setFontSize(26);
|
||||
|
||||
std::string textLine4 = "Press the SYNC Button on the Wii U console to exit.";
|
||||
DrawUtils::print(SCREEN_WIDTH / 2 + DrawUtils::getTextWidth(textLine4.c_str()) / 2, SCREEN_HEIGHT - 40, textLine4.c_str(), true);
|
||||
|
||||
DrawUtils::endDraw();
|
||||
}
|
||||
|
||||
PairMenu::PairMenu() {
|
||||
CCRSysInit();
|
||||
|
||||
mState = STATE_WAIT;
|
||||
mGamePadSyncTimeout = 120;
|
||||
|
||||
// Initialize IM
|
||||
mIMHandle = IM_Open();
|
||||
if (mIMHandle < 0) {
|
||||
DEBUG_FUNCTION_LINE_ERR("PairMenu: IM_Open failed");
|
||||
OSFatal("AutobootModule: PairMenu: IM_Open failed");
|
||||
}
|
||||
mIMRequest = (IMRequest *) memalign(0x40, sizeof(IMRequest));
|
||||
|
||||
// Allocate a separate request for IM_CancelGetEventNotify to avoid conflict with the pending IM_GetEventNotify request
|
||||
mIMCancelRequest = (IMRequest *) memalign(0x40, sizeof(IMRequest));
|
||||
|
||||
if (!mIMRequest || !mIMCancelRequest) {
|
||||
DEBUG_FUNCTION_LINE_ERR("Failed to allocate im request");
|
||||
OSFatal("AutobootModule: PairMenu: Failed to allocate im request");
|
||||
}
|
||||
|
||||
mIMEventMask = IM_EVENT_SYNC;
|
||||
|
||||
// Notify about sync button events
|
||||
IM_GetEventNotify(mIMHandle, mIMRequest, &mIMEventMask, PairMenu::SyncButtonCallback, this);
|
||||
}
|
||||
|
||||
PairMenu::~PairMenu() {
|
||||
// Close IM
|
||||
IM_CancelGetEventNotify(mIMHandle, mIMCancelRequest, nullptr, nullptr);
|
||||
IM_Close(mIMHandle);
|
||||
if (mIMCancelRequest) {
|
||||
free(mIMCancelRequest);
|
||||
mIMCancelRequest = {};
|
||||
}
|
||||
if (mIMRequest) {
|
||||
free(mIMRequest);
|
||||
mIMRequest = {};
|
||||
}
|
||||
|
||||
// Deinit CCRSys
|
||||
CCRSysExit();
|
||||
}
|
||||
|
||||
bool PairMenu::ProcessPairScreen() {
|
||||
switch (mState) {
|
||||
case STATE_SYNC_WPAD: {
|
||||
// WPAD syncing stops after ~18 seconds, make sure to restart it.
|
||||
if ((uint32_t) OSTicksToSeconds(OSGetTime() - mSyncWPADStartTime) >= 18) {
|
||||
WPADStartSyncDevice();
|
||||
mSyncWPADStartTime = OSGetTime();
|
||||
}
|
||||
|
||||
InputUtils::InputData input = InputUtils::getControllerInput();
|
||||
|
||||
// Stop syncing when pressing A or B.
|
||||
if (input.trigger & (VPAD_BUTTON_A | VPAD_BUTTON_B)) {
|
||||
mState = STATE_WAIT;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case STATE_SYNC_GAMEPAD: {
|
||||
if (CCRSysGetPincode(&mGamePadPincode) != 0) {
|
||||
DEBUG_FUNCTION_LINE_ERR("CCRSysGetPincode failed");
|
||||
mState = STATE_WAIT;
|
||||
break;
|
||||
}
|
||||
|
||||
// Start pairing to slot 1 (second gamepad)
|
||||
if (CCRSysStartPairing(0, mGamePadSyncTimeout) != 0) {
|
||||
DEBUG_FUNCTION_LINE_ERR("CCRSysStartPairing failed.");
|
||||
mState = STATE_WAIT;
|
||||
break;
|
||||
}
|
||||
|
||||
// Pairing has started, save start time
|
||||
mSyncGamePadStartTime = OSGetTime();
|
||||
mState = STATE_PAIRING;
|
||||
|
||||
DEBUG_FUNCTION_LINE("Started GamePad syncing.");
|
||||
|
||||
break;
|
||||
}
|
||||
case STATE_PAIRING: {
|
||||
// Get the current pairing state
|
||||
CCRSysPairingState pairingState = CCRSysGetPairingState();
|
||||
if (pairingState == CCR_SYS_PAIRING_TIMED_OUT) {
|
||||
DEBUG_FUNCTION_LINE("GamePad SYNC timed out.");
|
||||
// Pairing has timed out or was cancelled
|
||||
CCRSysStopPairing();
|
||||
mState = STATE_WAIT;
|
||||
} else if (pairingState == CCR_SYS_PAIRING_FINISHED) {
|
||||
DEBUG_FUNCTION_LINE("GamePad paired.");
|
||||
mState = STATE_WAIT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case STATE_CANCEL: {
|
||||
CCRSysStopPairing();
|
||||
mState = STATE_WAIT;
|
||||
break;
|
||||
}
|
||||
case STATE_WAIT:
|
||||
break;
|
||||
}
|
||||
switch (mState) {
|
||||
case STATE_WAIT: {
|
||||
return false;
|
||||
}
|
||||
case STATE_SYNC_WPAD:
|
||||
drawPairKPADScreen();
|
||||
break;
|
||||
case STATE_SYNC_GAMEPAD:
|
||||
case STATE_PAIRING:
|
||||
case STATE_CANCEL: {
|
||||
drawPairScreen();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void PairMenu::SyncButtonCallback(IOSError error, void *arg) {
|
||||
auto *pairMenu = (PairMenu *) arg;
|
||||
|
||||
if (error == IOS_ERROR_OK && pairMenu && (pairMenu->mIMEventMask & IM_EVENT_SYNC)) {
|
||||
if (pairMenu->mState == STATE_WAIT) {
|
||||
pairMenu->mState = STATE_SYNC_WPAD;
|
||||
// We need to restart the WPAD pairing every 18 seconds. For the timing we need to save the current time.
|
||||
pairMenu->mSyncWPADStartTime = OSGetTime();
|
||||
} else if (pairMenu->mState == STATE_SYNC_WPAD) {
|
||||
pairMenu->mState = STATE_SYNC_GAMEPAD;
|
||||
} else if (pairMenu->mState == STATE_SYNC_GAMEPAD || pairMenu->mState == STATE_PAIRING) {
|
||||
pairMenu->mState = STATE_CANCEL;
|
||||
}
|
||||
OSMemoryBarrier();
|
||||
IM_GetEventNotify(pairMenu->mIMHandle, pairMenu->mIMRequest, &pairMenu->mIMEventMask, PairMenu::SyncButtonCallback, pairMenu);
|
||||
}
|
||||
}
|
45
source/PairUtils.h
Normal file
45
source/PairUtils.h
Normal file
@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include "MenuUtils.h"
|
||||
#include "logger.h"
|
||||
#include <coreinit/cache.h>
|
||||
#include <coreinit/im.h>
|
||||
#include <coreinit/ios.h>
|
||||
#include <coreinit/time.h>
|
||||
#include <malloc.h>
|
||||
#include <nn/ccr/sys.h>
|
||||
|
||||
|
||||
class PairMenu {
|
||||
public:
|
||||
PairMenu();
|
||||
|
||||
~PairMenu();
|
||||
|
||||
bool ProcessPairScreen();
|
||||
|
||||
static void SyncButtonCallback(IOSError error, void *arg);
|
||||
|
||||
void drawPairScreen() const;
|
||||
|
||||
void drawPairKPADScreen() const;
|
||||
|
||||
private:
|
||||
enum PairMenuState {
|
||||
STATE_WAIT, // Wait for SYNC button press
|
||||
STATE_SYNC_WPAD,
|
||||
STATE_SYNC_GAMEPAD,
|
||||
STATE_PAIRING,
|
||||
STATE_CANCEL,
|
||||
};
|
||||
|
||||
IOSHandle mIMHandle{};
|
||||
IMRequest *mIMRequest{};
|
||||
IMRequest *mIMCancelRequest{};
|
||||
OSTime mSyncWPADStartTime = 0;
|
||||
OSTime mSyncGamePadStartTime = 0;
|
||||
uint32_t mGamePadPincode = 0;
|
||||
PairMenuState mState = STATE_WAIT;
|
||||
uint32_t mGamePadSyncTimeout = 120;
|
||||
IMEventMask mIMEventMask{};
|
||||
};
|
@ -1,9 +1,10 @@
|
||||
#include <malloc.h>
|
||||
|
||||
#include "BootUtils.h"
|
||||
#include "QuickStartUtils.h"
|
||||
#include "BootUtils.h"
|
||||
#include "MenuUtils.h"
|
||||
#include "logger.h"
|
||||
|
||||
#include "utils/SplashScreenDrawer.h"
|
||||
#include "utils/SplashSoundPlayer.h"
|
||||
#include "utils/gfx.h"
|
||||
#include <coreinit/exit.h>
|
||||
#include <coreinit/foreground.h>
|
||||
#include <coreinit/memdefaultheap.h>
|
||||
@ -12,17 +13,23 @@
|
||||
#include <nn/act/client_cpp.h>
|
||||
#include <nn/ccr/sys_caffeine.h>
|
||||
#include <nn/sl.h>
|
||||
#include <nsysccr/cdc.h>
|
||||
#include <optional>
|
||||
#include <proc_ui/procui.h>
|
||||
#include <rpxloader/rpxloader.h>
|
||||
#include <sysapp/launch.h>
|
||||
#include <sysapp/title.h>
|
||||
|
||||
extern "C" void __fini_wut();
|
||||
|
||||
#define UPPER_TITLE_ID_HOMEBREW 0x0005000F
|
||||
#define TITLE_ID_HOMEBREW_MASK (((uint64_t) UPPER_TITLE_ID_HOMEBREW) << 32)
|
||||
|
||||
static void StartAppletAndExit() {
|
||||
DEBUG_FUNCTION_LINE("Wait for applet");
|
||||
ProcUIInit(OSSavesDone_ReadyToRelease);
|
||||
|
||||
bool doProcUi = true;
|
||||
bool doProcUi = true;
|
||||
bool launchWiiUMenuOnNextForeground = false;
|
||||
while (true) {
|
||||
switch (ProcUIProcessMessages(true)) {
|
||||
@ -62,10 +69,6 @@ static void StartAppletAndExit() {
|
||||
_Exit(0);
|
||||
}
|
||||
|
||||
extern "C" int32_t SYSSwitchToBrowser(void *);
|
||||
extern "C" int32_t SYSSwitchToEShop(void *);
|
||||
extern "C" int32_t _SYSSwitchTo(uint32_t pfid);
|
||||
|
||||
void loadConsoleAccount(const char *data_uuid) {
|
||||
nn::act::Initialize();
|
||||
for (int32_t i = 0; i < 13; i++) {
|
||||
@ -82,37 +85,150 @@ void loadConsoleAccount(const char *data_uuid) {
|
||||
nn::act::Finalize();
|
||||
}
|
||||
|
||||
bool getQuickBoot() {
|
||||
class FileStreamWrapper {
|
||||
public:
|
||||
static std::unique_ptr<FileStreamWrapper> CreateFromPath(std::string_view path, std::string_view mode = "r") {
|
||||
return std::unique_ptr<FileStreamWrapper>(new FileStreamWrapper(path, mode));
|
||||
}
|
||||
|
||||
~FileStreamWrapper() {
|
||||
mFileStream.reset();
|
||||
FSDelClient(&mFsClient, FS_ERROR_FLAG_NONE);
|
||||
}
|
||||
|
||||
nn::sl::details::IStreamBase &GetStream() {
|
||||
return *mFileStream;
|
||||
}
|
||||
|
||||
private:
|
||||
explicit FileStreamWrapper(std::string_view path, std::string_view mode) {
|
||||
FSAddClient(&mFsClient, FS_ERROR_FLAG_NONE);
|
||||
FSInitCmdBlock(&mCmdBlock);
|
||||
mFileStream = std::make_unique<nn::sl::FileStream>();
|
||||
mFileStream->Initialize(&mFsClient, &mCmdBlock, path.data(), mode.data());
|
||||
}
|
||||
|
||||
std::unique_ptr<nn::sl::FileStream> mFileStream{};
|
||||
FSClient mFsClient{};
|
||||
FSCmdBlock mCmdBlock{};
|
||||
};
|
||||
|
||||
static bool sQuickStartTitleSelected = false;
|
||||
|
||||
class QuickStartAutoAbort {
|
||||
public:
|
||||
QuickStartAutoAbort() {
|
||||
OSCreateAlarm(&mDRCConnectedAlarm);
|
||||
OSSetAlarmUserData(&mDRCConnectedAlarm, &disconnectedCount);
|
||||
OSSetPeriodicAlarm(&mDRCConnectedAlarm,
|
||||
OSGetTime() + OSSecondsToTicks(10),
|
||||
OSSecondsToTicks(3),
|
||||
&AbortOnDRCDisconnect);
|
||||
OSCreateAlarm(&mAlarm);
|
||||
OSSetAlarm(&mAlarm, OSSecondsToTicks(120), AbortQuickStartTitle);
|
||||
mDRCConnected = IsDRCConnected();
|
||||
}
|
||||
|
||||
~QuickStartAutoAbort() {
|
||||
OSCancelAlarm(&mDRCConnectedAlarm);
|
||||
OSCancelAlarm(&mAlarm);
|
||||
|
||||
OSWaitAlarm(&mDRCConnectedAlarm);
|
||||
OSWaitAlarm(&mAlarm);
|
||||
|
||||
// Reconnect the DRC if it was connected at launch but then disconnected;
|
||||
if (mDRCConnected && !IsDRCConnected()) {
|
||||
DEBUG_FUNCTION_LINE("Wake up GamePad");
|
||||
CCRCDCWowlWakeDrcArg args = {.state = 1};
|
||||
CCRCDCWowlWakeDrc(&args);
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsDRCConnected() {
|
||||
CCRCDCDrcState state = {};
|
||||
if (CCRCDCSysGetDrcState(CCR_CDC_DESTINATION_DRC0, &state) != 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void AbortQuickStartTitle(OSAlarm *alarm, OSContext *) {
|
||||
DEBUG_FUNCTION_LINE_INFO("Canceling quick start menu after 2 minutes");
|
||||
CCRSysCaffeineBootCheckAbort();
|
||||
}
|
||||
|
||||
static void AbortOnDRCDisconnect(OSAlarm *alarm, OSContext *) {
|
||||
if (sQuickStartTitleSelected) {
|
||||
return;
|
||||
}
|
||||
if (!IsDRCConnected()) {
|
||||
int *disconnectedCount = (int *) OSGetAlarmUserData(alarm);
|
||||
if (++(*disconnectedCount) >= 2) {
|
||||
DEBUG_FUNCTION_LINE_INFO("GamePad was disconnected, lets abort the quick start menu");
|
||||
CCRSysCaffeineBootCheckAbort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
OSAlarm mDRCConnectedAlarm{};
|
||||
OSAlarm mAlarm{};
|
||||
bool mDRCConnected = false;
|
||||
int disconnectedCount = 0;
|
||||
};
|
||||
|
||||
bool launchQuickStartTitle() {
|
||||
// Automatically abort quick start if selecting takes longer than 120 seconds or the DRC disconnects
|
||||
QuickStartAutoAbort quickStartAutoAbort;
|
||||
|
||||
// Waits until the quick start menu has been closed.
|
||||
auto bootCheck = CCRSysCaffeineBootCheck();
|
||||
if (bootCheck == 0) {
|
||||
sQuickStartTitleSelected = true;
|
||||
|
||||
nn::sl::Initialize(MEMAllocFromDefaultHeapEx, MEMFreeToDefaultHeap);
|
||||
char path[0x80];
|
||||
nn::sl::GetDefaultDatabasePath(path, 0x80, 0x0005001010066000);// ECO process
|
||||
FSCmdBlock cmdBlock;
|
||||
FSInitCmdBlock(&cmdBlock);
|
||||
nn::sl::GetDefaultDatabasePath(path, 0x80, 0x0005001010066000); // ECO process
|
||||
nn::sl::LaunchInfoDatabase launchInfoDatabase;
|
||||
nn::sl::LaunchInfo info;
|
||||
{
|
||||
// In theory the region doesn't even matter.
|
||||
// The region is used to load a "system table" into the LaunchInfoDatabase which provides the LaunchInfos for
|
||||
// the Wii U Menu and System Settings. In the code below we check for all possible System Settings title id and
|
||||
// have a fallback to the Wii U Menu... This means we could get away a wrong region, but let's use the correct one
|
||||
// anyway
|
||||
const auto region = []() {
|
||||
if (SYSCheckTitleExists(0x0005001010047000L)) { // JPN System Settings
|
||||
return nn::sl::REGION_JPN;
|
||||
} else if (SYSCheckTitleExists(0x0005001010047100L)) { // USA System Settings
|
||||
return nn::sl::REGION_USA;
|
||||
} else if (SYSCheckTitleExists(0x0005001010047200L)) { // EUR System Settings
|
||||
return nn::sl::REGION_EUR;
|
||||
}
|
||||
return nn::sl::REGION_EUR;
|
||||
}();
|
||||
auto fileStream = FileStreamWrapper::CreateFromPath(path);
|
||||
if (launchInfoDatabase.Load(fileStream->GetStream(), region).IsFailure()) {
|
||||
DEBUG_FUNCTION_LINE_ERR("Failed to load LaunchInfoDatabase");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
auto fileStream = new nn::sl::FileStream;
|
||||
auto *fsClient = (FSClient *) memalign(0x40, sizeof(FSClient));
|
||||
memset(fsClient, 0, sizeof(*fsClient));
|
||||
FSAddClient(fsClient, FS_ERROR_FLAG_NONE);
|
||||
|
||||
fileStream->Initialize(fsClient, &cmdBlock, path, "r");
|
||||
|
||||
auto database = new nn::sl::LaunchInfoDatabase;
|
||||
database->Load(fileStream, nn::sl::REGION_EUR);
|
||||
|
||||
CCRAppLaunchParam data;// load sys caffeine data
|
||||
CCRAppLaunchParam data; // load sys caffeine data
|
||||
// load app launch param
|
||||
CCRSysCaffeineGetAppLaunchParam(&data);
|
||||
|
||||
// get launch info for id
|
||||
nn::sl::LaunchInfo info;
|
||||
auto result = database->GetLaunchInfoById(&info, data.titleId);
|
||||
if (data.launchInfoDatabaseEntryId == 1) { // This id is hardcoded into the nn_sl.rpl
|
||||
DEBUG_FUNCTION_LINE("Launch Quick Start Settings");
|
||||
SysAppSettingsArgs args{};
|
||||
args.jumpTo = SYS_SETTINGS_JUMP_TO_QUICK_START_SETTINGS; // quick start settings
|
||||
_SYSLaunchSettings(&args);
|
||||
return true;
|
||||
}
|
||||
|
||||
delete database;
|
||||
delete fileStream;
|
||||
loadConsoleAccount(data.uuid);
|
||||
|
||||
FSDelClient(fsClient, FS_ERROR_FLAG_NONE);
|
||||
auto result = launchInfoDatabase.GetLaunchInfoById(&info, data.launchInfoDatabaseEntryId);
|
||||
|
||||
nn::sl::Finalize();
|
||||
|
||||
@ -121,7 +237,16 @@ bool getQuickBoot() {
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG_FUNCTION_LINE("Trying to autoboot for titleId %016llX", info.titleId);
|
||||
if ((info.titleId & TITLE_ID_HOMEBREW_MASK) == TITLE_ID_HOMEBREW_MASK) {
|
||||
std::string homebrewPath = info.parameter;
|
||||
DEBUG_FUNCTION_LINE("Trying to launch homebrew title: \"%s\"", homebrewPath.c_str());
|
||||
|
||||
if (auto err = RPXLoader_LaunchHomebrew(homebrewPath.c_str()); err != RPX_LOADER_RESULT_SUCCESS) {
|
||||
DEBUG_FUNCTION_LINE_WARN("Failed to launch homebrew title: %s (%d)", RPXLoader_GetStatusStr(err), err);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (info.titleId == 0x0005001010040000L ||
|
||||
info.titleId == 0x0005001010040100L ||
|
||||
@ -130,6 +255,14 @@ bool getQuickBoot() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (info.titleId == 0x0005001010047000L ||
|
||||
info.titleId == 0x0005001010047100L ||
|
||||
info.titleId == 0x0005001010047200L) {
|
||||
DEBUG_FUNCTION_LINE("Launch System Settings");
|
||||
_SYSLaunchSettings(nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (info.titleId == 0x000500301001220AL ||
|
||||
info.titleId == 0x000500301001210AL ||
|
||||
info.titleId == 0x000500301001200AL) {
|
||||
@ -157,7 +290,7 @@ bool getQuickBoot() {
|
||||
info.titleId == 0x000500301001820AL) {
|
||||
DEBUG_FUNCTION_LINE("Launching the Download Management");
|
||||
loadConsoleAccount(data.uuid);
|
||||
_SYSSwitchTo(12);
|
||||
_SYSSwitchTo(SYSAPP_PFID_DOWNLOAD_MANAGEMENT);
|
||||
|
||||
StartAppletAndExit();
|
||||
|
||||
@ -168,7 +301,7 @@ bool getQuickBoot() {
|
||||
info.titleId == 0x000500301001620AL) {
|
||||
DEBUG_FUNCTION_LINE("Launching Miiverse");
|
||||
loadConsoleAccount(data.uuid);
|
||||
_SYSSwitchTo(9);
|
||||
_SYSSwitchTo(SYSAPP_PFID_MIIVERSE);
|
||||
|
||||
StartAppletAndExit();
|
||||
|
||||
@ -179,7 +312,7 @@ bool getQuickBoot() {
|
||||
info.titleId == 0x000500301001520AL) {
|
||||
DEBUG_FUNCTION_LINE("Launching Friendlist");
|
||||
loadConsoleAccount(data.uuid);
|
||||
_SYSSwitchTo(11);
|
||||
_SYSSwitchTo(SYSAPP_PFID_FRIENDLIST);
|
||||
|
||||
StartAppletAndExit();
|
||||
|
||||
@ -190,36 +323,66 @@ bool getQuickBoot() {
|
||||
info.titleId == 0x000500301001320AL) {
|
||||
DEBUG_FUNCTION_LINE("Launching TVii");
|
||||
loadConsoleAccount(data.uuid);
|
||||
_SYSSwitchTo(3);
|
||||
_SYSSwitchTo(SYSAPP_PFID_TVII);
|
||||
|
||||
StartAppletAndExit();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (info.titleId == 0x0005001010004000L) {// OSv0
|
||||
if (info.titleId == 0x0005001010004000L) { // OSv0
|
||||
DEBUG_FUNCTION_LINE("Launching vWii System Menu");
|
||||
bootvWiiMenu();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!SYSCheckTitleExists(info.titleId)) {
|
||||
DEBUG_FUNCTION_LINE("Title %016llX doesn't exist", info.titleId);
|
||||
return false;
|
||||
uint64_t titleIdToLaunch = info.titleId;
|
||||
|
||||
switch (info.mediaType) {
|
||||
case nn::sl::NN_SL_MEDIA_TYPE_ODD: {
|
||||
if (!handleDiscInsertScreen(titleIdToLaunch, &titleIdToLaunch)) {
|
||||
DEBUG_FUNCTION_LINE("Launch Wii U Menu!");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if (!SYSCheckTitleExists(titleIdToLaunch)) {
|
||||
DEBUG_FUNCTION_LINE("Title %016llX doesn't exist", titleIdToLaunch);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MCPTitleListType titleInfo;
|
||||
int32_t handle = MCP_Open();
|
||||
auto err = MCP_GetTitleInfo(handle, info.titleId, &titleInfo);
|
||||
auto err = MCP_GetTitleInfo(handle, titleIdToLaunch, &titleInfo);
|
||||
MCP_Close(handle);
|
||||
if (err == 0) {
|
||||
loadConsoleAccount(data.uuid);
|
||||
DEBUG_FUNCTION_LINE("Launch %016llX", titleIdToLaunch);
|
||||
char metaDir[256] = {};
|
||||
auto res = ACPGetTitleMetaDir(titleIdToLaunch, metaDir, sizeof(metaDir) - 1);
|
||||
if (res == ACP_RESULT_SUCCESS) {
|
||||
GfxInit();
|
||||
{
|
||||
SplashScreenDrawer splashScreenDrawer(metaDir);
|
||||
splashScreenDrawer.Draw();
|
||||
SplashSoundPlayer splashSound(metaDir);
|
||||
splashSound.Play();
|
||||
}
|
||||
GfxShutdown();
|
||||
} else {
|
||||
DEBUG_FUNCTION_LINE_WARN("Failed to find assets");
|
||||
}
|
||||
ACPAssignTitlePatch(&titleInfo);
|
||||
_SYSLaunchTitleWithStdArgsInNoSplash(info.titleId, nullptr);
|
||||
_SYSLaunchTitleByPathFromLauncher(titleInfo.path, strlen(titleInfo.path));
|
||||
return true;
|
||||
} else {
|
||||
DEBUG_FUNCTION_LINE_WARN("Failed to get title info");
|
||||
}
|
||||
|
||||
DEBUG_FUNCTION_LINE("Launch Wii U Menu!");
|
||||
return false;
|
||||
} else {
|
||||
DEBUG_FUNCTION_LINE("No quick start");
|
||||
|
@ -1,3 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
bool getQuickBoot();
|
||||
bool launchQuickStartTitle();
|
@ -29,21 +29,22 @@ static int numberUSBStorageDevicesConnected() {
|
||||
memset(handle, 0, sizeof(UhsHandle));
|
||||
auto *config = (UhsConfig *) memalign(0x40, sizeof(UhsConfig));
|
||||
if (!config) {
|
||||
free(config);
|
||||
free(handle);
|
||||
return -2;
|
||||
}
|
||||
memset(config, 0, sizeof(UhsConfig));
|
||||
|
||||
config->controller_num = 0;
|
||||
uint32_t size = 5120;
|
||||
void *buffer = memalign(0x40, size);
|
||||
if (buffer == nullptr) {
|
||||
uint32_t size = 5120;
|
||||
void *buffer = memalign(0x40, size);
|
||||
if (!buffer) {
|
||||
free(handle);
|
||||
free(config);
|
||||
return -3;
|
||||
}
|
||||
memset(buffer, 0, size);
|
||||
|
||||
config->buffer = buffer;
|
||||
config->buffer = buffer;
|
||||
config->buffer_size = size;
|
||||
|
||||
if (UhsClientOpen(handle, config) != UHS_STATUS_OK) {
|
||||
@ -51,7 +52,7 @@ static int numberUSBStorageDevicesConnected() {
|
||||
free(handle);
|
||||
free(config);
|
||||
free(buffer);
|
||||
return -3;
|
||||
return -4;
|
||||
}
|
||||
|
||||
UhsInterfaceProfile profiles[10];
|
||||
@ -65,7 +66,7 @@ static int numberUSBStorageDevicesConnected() {
|
||||
free(handle);
|
||||
free(config);
|
||||
free(buffer);
|
||||
return -4;
|
||||
return -5;
|
||||
}
|
||||
|
||||
auto found = 0;
|
||||
@ -79,7 +80,7 @@ static int numberUSBStorageDevicesConnected() {
|
||||
UhsClientClose(handle);
|
||||
free(handle);
|
||||
free(config);
|
||||
free(config->buffer);
|
||||
free(buffer);
|
||||
return found;
|
||||
}
|
||||
|
||||
@ -89,23 +90,21 @@ void initExternalStorage() {
|
||||
// the lib before actually using it.
|
||||
return;
|
||||
}
|
||||
int connectedStorage = 0;
|
||||
if ((connectedStorage = numberUSBStorageDevicesConnected()) <= 0) {
|
||||
nn::spm::Initialize();
|
||||
InitEmptyExternalStorage();
|
||||
nn::spm::Finalize();
|
||||
return;
|
||||
int numConnectedStorage;
|
||||
int maxTries = 1200; // Wait up to 20 seconds, like the Wii U Menu
|
||||
if ((numConnectedStorage = numberUSBStorageDevicesConnected()) <= 0) {
|
||||
maxTries = 1; // Only try once if no USBStorageDrive is connected
|
||||
} else {
|
||||
DEBUG_FUNCTION_LINE("Connected StorageDevices = %d", numConnectedStorage);
|
||||
}
|
||||
|
||||
DEBUG_FUNCTION_LINE("Connected StorageDevices = %d", connectedStorage);
|
||||
|
||||
nn::spm::Initialize();
|
||||
|
||||
nn::spm::StorageListItem items[0x20];
|
||||
int tries = 0;
|
||||
int tries = 0;
|
||||
bool found = false;
|
||||
|
||||
while (tries < 1200) {// Wait up to 20 seconds, like the Wii U Menu
|
||||
while (tries < maxTries) {
|
||||
int32_t numItems = nn::spm::GetStorageList(items, 0x20);
|
||||
|
||||
DEBUG_FUNCTION_LINE("Number of items: %d", numItems);
|
||||
@ -126,7 +125,7 @@ void initExternalStorage() {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (found || (connectedStorage == numItems)) {
|
||||
if (found || (numConnectedStorage == numItems)) {
|
||||
DEBUG_FUNCTION_LINE("Found all expected items, breaking.");
|
||||
break;
|
||||
}
|
||||
@ -134,7 +133,9 @@ void initExternalStorage() {
|
||||
tries++;
|
||||
}
|
||||
if (!found) {
|
||||
DEBUG_FUNCTION_LINE("USB Storage is connected but either it doesn't have a WFS partition or we ran into a timeout.");
|
||||
if (numConnectedStorage > 0) {
|
||||
DEBUG_FUNCTION_LINE("USB Storage is connected but either it doesn't have a WFS partition or we ran into a timeout.");
|
||||
}
|
||||
InitEmptyExternalStorage();
|
||||
}
|
||||
|
||||
|
@ -7,17 +7,17 @@
|
||||
#include <whb/log_udp.h>
|
||||
|
||||
uint32_t moduleLogInit = false;
|
||||
uint32_t cafeLogInit = false;
|
||||
uint32_t udpLogInit = false;
|
||||
uint32_t cafeLogInit = false;
|
||||
uint32_t udpLogInit = false;
|
||||
#endif
|
||||
|
||||
void initLogging() {
|
||||
#ifdef DEBUG
|
||||
if (!(moduleLogInit = WHBLogModuleInit())) {
|
||||
cafeLogInit = WHBLogCafeInit();
|
||||
udpLogInit = WHBLogUdpInit();
|
||||
udpLogInit = WHBLogUdpInit();
|
||||
}
|
||||
#endif// DEBUG
|
||||
#endif // DEBUG
|
||||
}
|
||||
|
||||
void deinitLogging() {
|
||||
@ -34,5 +34,5 @@ void deinitLogging() {
|
||||
WHBLogUdpDeinit();
|
||||
udpLogInit = false;
|
||||
}
|
||||
#endif// DEBUG
|
||||
#endif // DEBUG
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <coreinit/debug.h>
|
||||
#include <string.h>
|
||||
#include <whb/log.h>
|
||||
|
||||
@ -7,26 +8,60 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define LOG_APP_TYPE "M"
|
||||
#define LOG_APP_NAME "autoboot_module"
|
||||
|
||||
#define __FILENAME_X__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
|
||||
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILENAME_X__)
|
||||
|
||||
#define LOG(LOG_FUNC, FMT, ARGS...) LOG_EX_DEFAULT(LOG_FUNC, "", "", FMT, ##ARGS)
|
||||
|
||||
#define LOG_EX_DEFAULT(LOG_FUNC, LOG_LEVEL, LINE_END, FMT, ARGS...) LOG_EX(__FILENAME__, __FUNCTION__, __LINE__, LOG_FUNC, LOG_LEVEL, LINE_END, FMT, ##ARGS)
|
||||
|
||||
#define LOG_EX(FILENAME, FUNCTION, LINE, LOG_FUNC, LOG_LEVEL, LINE_END, FMT, ARGS...) \
|
||||
do { \
|
||||
LOG_FUNC("[(%s)%18s][%23s]%30s@L%04d: " LOG_LEVEL "" FMT "" LINE_END, LOG_APP_TYPE, LOG_APP_NAME, FILENAME, FUNCTION, LINE, ##ARGS); \
|
||||
} while (0)
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
#define __FILENAME_X__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
|
||||
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILENAME_X__)
|
||||
#ifdef VERBOSE_DEBUG
|
||||
#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) LOG(WHBLogPrintf, FMT, ##ARGS)
|
||||
#define DEBUG_FUNCTION_LINE_VERBOSE_EX(FILENAME, FUNCTION, LINE, FMT, ARGS...) LOG_EX(FILENAME, FUNCTION, LINE, WHBLogPrintf, "", "", FMT, ##ARGS);
|
||||
#define DEBUG_FUNCTION_LINE_LOADER_VERBOSE(FMT, ARGS...) LOG_EX_DEFAULT(((void (*)(const char *, ...))((uint32_t *) 0x010028d0)), "", "\n", FMT, ##ARGS)
|
||||
#else
|
||||
#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) while (0)
|
||||
#define DEBUG_FUNCTION_LINE_VERBOSE_EX(FMT, ARGS...) while (0)
|
||||
#define DEBUG_FUNCTION_LINE_LOADER_VERBOSE(FMT, ARGS...) while (0)
|
||||
#endif
|
||||
|
||||
#define DEBUG_FUNCTION_LINE(FMT, ARGS...) \
|
||||
do { \
|
||||
WHBLogPrintf("[%23s]%30s@L%04d: " FMT "", __FILENAME__, __FUNCTION__, __LINE__, ##ARGS); \
|
||||
} while (0)
|
||||
#define DEBUG_FUNCTION_LINE(FMT, ARGS...) LOG(WHBLogPrintf, FMT, ##ARGS)
|
||||
|
||||
#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) \
|
||||
do { \
|
||||
WHBLogWritef("[%23s]%30s@L%04d: " FMT "", __FILENAME__, __FUNCTION__, __LINE__, ##ARGS); \
|
||||
} while (0)
|
||||
#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) LOG(WHBLogWritef, FMT, ##ARGS)
|
||||
|
||||
#define DEBUG_FUNCTION_LINE_WARN(FMT, ARGS...) LOG_EX_DEFAULT(WHBLogPrintf, "## WARN## ", "", FMT, ##ARGS)
|
||||
#define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX_DEFAULT(WHBLogPrintf, "##ERROR## ", "", FMT, ##ARGS)
|
||||
#define DEBUG_FUNCTION_LINE_INFO(FMT, ARGS...) LOG_EX_DEFAULT(WHBLogPrintf, "## INFO## ", "", FMT, ##ARGS)
|
||||
|
||||
#define DEBUG_FUNCTION_LINE_ERR_LAMBDA(FILENAME, FUNCTION, LINE, FMT, ARGS...) LOG_EX(FILENAME, FUNCTION, LINE, WHBLogPrintf, "##ERROR## ", "", FMT, ##ARGS);
|
||||
|
||||
#else
|
||||
|
||||
#define DEBUG_FUNCTION_LINE(FMT, ARGS...) while (0)
|
||||
#define DEBUG_FUNCTION_LINE_VERBOSE_EX(FMT, ARGS...) while (0)
|
||||
|
||||
#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) while (0)
|
||||
#define DEBUG_FUNCTION_LINE_VERBOSE(FMT, ARGS...) while (0)
|
||||
|
||||
#define DEBUG_FUNCTION_LINE_LOADER_VERBOSE(FMT, ARGS...) while (0)
|
||||
|
||||
#define DEBUG_FUNCTION_LINE(FMT, ARGS...) while (0)
|
||||
|
||||
#define DEBUG_FUNCTION_LINE_WRITE(FMT, ARGS...) while (0)
|
||||
|
||||
#define DEBUG_FUNCTION_LINE_WARN(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "## WARN## ", "\n", FMT, ##ARGS)
|
||||
#define DEBUG_FUNCTION_LINE_ERR(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "##ERROR## ", "\n", FMT, ##ARGS)
|
||||
#define DEBUG_FUNCTION_LINE_INFO(FMT, ARGS...) LOG_EX_DEFAULT(OSReport, "## INFO## ", "\n", FMT, ##ARGS)
|
||||
|
||||
#define DEBUG_FUNCTION_LINE_ERR_LAMBDA(FILENAME, FUNCTION, LINE, FMT, ARGS...) LOG_EX(FILENAME, FUNCTION, LINE, OSReport, "##ERROR## ", "\n", FMT, ##ARGS);
|
||||
|
||||
#endif
|
||||
|
||||
|
137
source/main.cpp
137
source/main.cpp
@ -1,55 +1,124 @@
|
||||
#include "BootUtils.h"
|
||||
#include "DrawUtils.h"
|
||||
#include "InputUtils.h"
|
||||
#include "MenuUtils.h"
|
||||
#include "QuickStartUtils.h"
|
||||
#include "StorageUtils.h"
|
||||
#include "logger.h"
|
||||
#include <coreinit/debug.h>
|
||||
#include <gx2/state.h>
|
||||
#include <malloc.h>
|
||||
#include <coreinit/filesystem_fsa.h>
|
||||
#include <coreinit/thread.h>
|
||||
#include <coreinit/title.h>
|
||||
#include <mocha/mocha.h>
|
||||
#include <rpxloader/rpxloader.h>
|
||||
#include <sndcore2/core.h>
|
||||
#include <string>
|
||||
#include <sys/stat.h>
|
||||
#include <sysapp/launch.h>
|
||||
#include <vpad/input.h>
|
||||
|
||||
#include "BootUtils.h"
|
||||
#include "MenuUtils.h"
|
||||
|
||||
void clearScreen() {
|
||||
auto buffer = DrawUtils::InitOSScreen();
|
||||
if (!buffer) {
|
||||
OSFatal("Failed to alloc memory for screen");
|
||||
}
|
||||
DrawUtils::clear(COLOR_BACKGROUND);
|
||||
|
||||
// Call GX2Init to shut down OSScreen
|
||||
GX2Init(nullptr);
|
||||
|
||||
free(buffer);
|
||||
}
|
||||
bool gUpdatesBlocked = false;
|
||||
|
||||
int32_t main(int32_t argc, char **argv) {
|
||||
initLogging();
|
||||
DEBUG_FUNCTION_LINE("Hello from Autoboot Module");
|
||||
|
||||
// Clear screen to avoid screen corruptions when loading the Wii U Menu
|
||||
clearScreen();
|
||||
InputUtils::Init();
|
||||
|
||||
initExternalStorage();
|
||||
|
||||
if (getQuickBoot()) {
|
||||
// Use librpxloader.
|
||||
RPXLoaderStatus error3;
|
||||
if ((error3 = RPXLoader_InitLibrary()) != RPX_LOADER_RESULT_SUCCESS) {
|
||||
DEBUG_FUNCTION_LINE_ERR("AutobootModule: Failed to init RPXLoader. This can be ignored when not running Aroma. Error %s [%d]", RPXLoader_GetStatusStr(error3), error3);
|
||||
}
|
||||
|
||||
// If we are in System Transfer context we need to restart the app to actually
|
||||
if (OSGetTitleID() == 0x0005001010062000L || OSGetTitleID() == 0x0005001010062100L || OSGetTitleID() == 0x0005001010062200L) {
|
||||
_SYSLaunchTitleWithStdArgsInNoSplash(OSGetTitleID(), nullptr);
|
||||
deinitLogging();
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string configPath = "fs:/vol/exernal01/wiiu/autoboot.cfg";
|
||||
if (launchQuickStartTitle()) {
|
||||
deinitLogging();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (Mocha_InitLibrary() != MOCHA_RESULT_SUCCESS) {
|
||||
OSFatal("AutobootModule: Mocha_InitLibrary failed");
|
||||
}
|
||||
|
||||
InputUtils::InputData buttons = InputUtils::getControllerInput();
|
||||
|
||||
bool hadMenu = false;
|
||||
|
||||
FSAInit();
|
||||
auto client = FSAAddClient(nullptr);
|
||||
if (client > 0) {
|
||||
if (Mocha_UnlockFSClientEx(client) == MOCHA_RESULT_SUCCESS) {
|
||||
// test if the update folder exists
|
||||
FSADirectoryHandle dirHandle{};
|
||||
if (FSAOpenDir(client, "/vol/storage_mlc01/sys/update", &dirHandle) >= 0) {
|
||||
FSACloseDir(client, dirHandle);
|
||||
gUpdatesBlocked = false;
|
||||
if (!AXIsInit()) {
|
||||
AXInit();
|
||||
}
|
||||
handleUpdateWarningScreen();
|
||||
hadMenu = true;
|
||||
} else {
|
||||
FSAStat st{};
|
||||
if (FSAGetStat(client, "/vol/storage_mlc01/sys/update", &st) != FS_ERROR_OK) {
|
||||
DEBUG_FUNCTION_LINE_INFO("Created \"/vol/storage_mlc01/sys/update\" as file");
|
||||
FSAFileHandle fd;
|
||||
FSAOpenFileEx(client, "/vol/storage_mlc01/sys/update", "w", static_cast<FSMode>(0x666), FS_OPEN_FLAG_NONE, 0, &fd);
|
||||
}
|
||||
gUpdatesBlocked = true;
|
||||
}
|
||||
} else {
|
||||
DEBUG_FUNCTION_LINE_ERR("Failed to unlock FSA Client");
|
||||
}
|
||||
|
||||
FSADelClient(client);
|
||||
} else {
|
||||
DEBUG_FUNCTION_LINE_ERR("Failed to create FSA Client");
|
||||
}
|
||||
|
||||
bool showvHBL = getVWiiHBLTitleId() != 0;
|
||||
bool showHBL = false;
|
||||
std::string configPath = "fs:/vol/external01/wiiu/autoboot.cfg";
|
||||
if (argc >= 1) {
|
||||
configPath = std::string(argv[0]) + "/autoboot.cfg";
|
||||
|
||||
auto hblInstallerPath = std::string(argv[0]) + "/modules/setup/50_hbl_installer.rpx";
|
||||
struct stat st {};
|
||||
if (stat(hblInstallerPath.c_str(), &st) >= 0) {
|
||||
showHBL = true;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t bootSelection = readAutobootOption(configPath);
|
||||
|
||||
VPADStatus vpad{};
|
||||
VPADRead(VPAD_CHAN_0, &vpad, 1, nullptr);
|
||||
std::map<uint32_t, std::string> menu;
|
||||
menu[BOOT_OPTION_WII_U_MENU] = "Wii U Menu";
|
||||
if (showHBL) {
|
||||
menu[BOOT_OPTION_HOMEBREW_LAUNCHER] = "Homebrew Launcher";
|
||||
}
|
||||
menu[BOOT_OPTION_VWII_SYSTEM_MENU] = "vWii System Menu";
|
||||
if (showvHBL) {
|
||||
menu[BOOT_OPTION_VWII_HOMEBREW_CHANNEL] = "vWii Homebrew Channel";
|
||||
}
|
||||
|
||||
if ((bootSelection == -1) || (vpad.hold & VPAD_BUTTON_PLUS)) {
|
||||
bootSelection = handleMenuScreen(configPath, bootSelection);
|
||||
if ((bootSelection == -1) ||
|
||||
(bootSelection == BOOT_OPTION_HOMEBREW_LAUNCHER && !showHBL) ||
|
||||
(bootSelection == BOOT_OPTION_VWII_HOMEBREW_CHANNEL && !showvHBL) ||
|
||||
(buttons.hold & VPAD_BUTTON_PLUS)) {
|
||||
if (!AXIsInit()) {
|
||||
AXInit();
|
||||
}
|
||||
bootSelection = handleMenuScreen(configPath, bootSelection, menu);
|
||||
hadMenu = true;
|
||||
}
|
||||
|
||||
if (bootSelection >= 0) {
|
||||
@ -58,12 +127,20 @@ int32_t main(int32_t argc, char **argv) {
|
||||
bootWiiUMenu();
|
||||
break;
|
||||
case BOOT_OPTION_HOMEBREW_LAUNCHER:
|
||||
if (!showHBL) {
|
||||
bootWiiUMenu();
|
||||
break;
|
||||
}
|
||||
bootHomebrewLauncher();
|
||||
break;
|
||||
case BOOT_OPTION_VWII_SYSTEM_MENU:
|
||||
bootvWiiMenu();
|
||||
break;
|
||||
case BOOT_OPTION_VWII_HOMEBREW_CHANNEL:
|
||||
if (!showvHBL) {
|
||||
bootvWiiMenu();
|
||||
break;
|
||||
}
|
||||
bootHomebrewChannel();
|
||||
break;
|
||||
default:
|
||||
@ -74,6 +151,16 @@ int32_t main(int32_t argc, char **argv) {
|
||||
bootWiiUMenu();
|
||||
}
|
||||
|
||||
InputUtils::DeInit();
|
||||
Mocha_DeInitLibrary();
|
||||
deinitLogging();
|
||||
if (AXIsInit()) {
|
||||
AXQuit();
|
||||
}
|
||||
|
||||
if (!hadMenu) {
|
||||
DrawUtils::ClearSavedFrameBuffers();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
extern bool gUpdatesBlocked;
|
1460
source/schrift.c
Normal file
1460
source/schrift.c
Normal file
File diff suppressed because it is too large
Load Diff
88
source/schrift.h
Normal file
88
source/schrift.h
Normal file
@ -0,0 +1,88 @@
|
||||
/* This file is part of libschrift.
|
||||
*
|
||||
* © 2019-2022 Thomas Oltmann and contributors
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
|
||||
|
||||
#ifndef SCHRIFT_H
|
||||
#define SCHRIFT_H 1
|
||||
|
||||
#include <stddef.h> /* size_t */
|
||||
#include <stdint.h> /* uint_fast32_t, uint_least32_t */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define SFT_DOWNWARD_Y 0x01
|
||||
|
||||
typedef struct SFT SFT;
|
||||
typedef struct SFT_Font SFT_Font;
|
||||
typedef uint_least32_t SFT_UChar; /* Guaranteed to be compatible with char32_t. */
|
||||
typedef uint_fast32_t SFT_Glyph;
|
||||
typedef struct SFT_LMetrics SFT_LMetrics;
|
||||
typedef struct SFT_GMetrics SFT_GMetrics;
|
||||
typedef struct SFT_Kerning SFT_Kerning;
|
||||
typedef struct SFT_Image SFT_Image;
|
||||
|
||||
struct SFT {
|
||||
SFT_Font *font;
|
||||
double xScale;
|
||||
double yScale;
|
||||
double xOffset;
|
||||
double yOffset;
|
||||
int flags;
|
||||
};
|
||||
|
||||
struct SFT_LMetrics {
|
||||
double ascender;
|
||||
double descender;
|
||||
double lineGap;
|
||||
};
|
||||
|
||||
struct SFT_GMetrics {
|
||||
double advanceWidth;
|
||||
double leftSideBearing;
|
||||
int yOffset;
|
||||
int minWidth;
|
||||
int minHeight;
|
||||
};
|
||||
|
||||
struct SFT_Kerning {
|
||||
double xShift;
|
||||
double yShift;
|
||||
};
|
||||
|
||||
struct SFT_Image {
|
||||
void *pixels;
|
||||
int width;
|
||||
int height;
|
||||
};
|
||||
|
||||
const char *sft_version(void);
|
||||
|
||||
SFT_Font *sft_loadmem(const void *mem, size_t size);
|
||||
void sft_freefont(SFT_Font *font);
|
||||
|
||||
int sft_lmetrics(const SFT *sft, SFT_LMetrics *metrics);
|
||||
int sft_lookup(const SFT *sft, SFT_UChar codepoint, SFT_Glyph *glyph);
|
||||
int sft_gmetrics(const SFT *sft, SFT_Glyph glyph, SFT_GMetrics *metrics);
|
||||
int sft_kerning(const SFT *sft, SFT_Glyph leftGlyph, SFT_Glyph rightGlyph,
|
||||
SFT_Kerning *kerning);
|
||||
int sft_render(const SFT *sft, SFT_Glyph glyph, SFT_Image image);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
108
source/utils.cpp
Normal file
108
source/utils.cpp
Normal file
@ -0,0 +1,108 @@
|
||||
#include "logger.h"
|
||||
#include <coreinit/filesystem_fsa.h>
|
||||
#include <coreinit/mcp.h>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <mocha/mocha.h>
|
||||
#include <string_view>
|
||||
#include <sys/stat.h>
|
||||
#include <vector>
|
||||
#include <whb/log.h>
|
||||
|
||||
bool GetTitleIdOfDisc(uint64_t *titleId, bool *discPresent) {
|
||||
if (discPresent) {
|
||||
*discPresent = false;
|
||||
}
|
||||
alignas(0x40) MCPTitleListType titles[4];
|
||||
uint32_t count = 0;
|
||||
|
||||
int handle = MCP_Open();
|
||||
if (handle < 0) {
|
||||
DEBUG_FUNCTION_LINE_ERR("MCP_Open failed");
|
||||
return false;
|
||||
}
|
||||
auto res = MCP_TitleListByDeviceType(handle, MCP_DEVICE_TYPE_ODD, &count, titles, sizeof(titles));
|
||||
MCP_Close(handle);
|
||||
|
||||
if (res >= 0 && count > 0) {
|
||||
if (discPresent) {
|
||||
*discPresent = true;
|
||||
}
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
if ((titles[i].titleId & 0xFFFFFFFF00000000L) == (0x0005000000000000)) {
|
||||
if (titleId) {
|
||||
*titleId = titles[i].titleId;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DeleteMLCUpdateDirectory() {
|
||||
bool result = false;
|
||||
auto client = FSAAddClient(nullptr);
|
||||
if (client > 0) {
|
||||
if (Mocha_UnlockFSClientEx(client) == MOCHA_RESULT_SUCCESS) {
|
||||
if (FSARemove(client, "/vol/storage_mlc01/sys/update") != FS_ERROR_OK) {
|
||||
DEBUG_FUNCTION_LINE_ERR("Failed to remove update directory");
|
||||
} else {
|
||||
FSAFileHandle fd;
|
||||
if (FSAOpenFileEx(client, "/vol/storage_mlc01/sys/update", "w", static_cast<FSMode>(0x666), FS_OPEN_FLAG_NONE, 0, &fd) != FS_ERROR_OK) {
|
||||
DEBUG_FUNCTION_LINE_WARN("Failed to create update file");
|
||||
}
|
||||
result = true;
|
||||
}
|
||||
} else {
|
||||
DEBUG_FUNCTION_LINE_ERR("Failed to unlock FSA Client");
|
||||
}
|
||||
FSADelClient(client);
|
||||
} else {
|
||||
DEBUG_FUNCTION_LINE_ERR("Failed to create FSA Client");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool RestoreMLCUpdateDirectory() {
|
||||
bool result = false;
|
||||
auto client = FSAAddClient(nullptr);
|
||||
if (client > 0) {
|
||||
if (Mocha_UnlockFSClientEx(client) == MOCHA_RESULT_SUCCESS) {
|
||||
FSARemove(client, "/vol/storage_mlc01/sys/update"); // Remove any existing files
|
||||
if (FSAMakeDir(client, "/vol/storage_mlc01/sys/update", static_cast<FSMode>(0x666)) != FS_ERROR_OK) {
|
||||
DEBUG_FUNCTION_LINE_WARN("Failed to restore update directory");
|
||||
}
|
||||
result = true;
|
||||
} else {
|
||||
DEBUG_FUNCTION_LINE_ERR("Failed to unlock FSA Client");
|
||||
}
|
||||
FSADelClient(client);
|
||||
} else {
|
||||
DEBUG_FUNCTION_LINE_ERR("Failed to create FSA Client");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
bool LoadFileIntoBuffer(std::string_view path, std::vector<uint8_t> &buffer) {
|
||||
struct stat st {};
|
||||
if (stat(path.data(), &st) < 0 || !S_ISREG(st.st_mode)) {
|
||||
DEBUG_FUNCTION_LINE_INFO("\"%s\" doesn't exists", path.data());
|
||||
return false;
|
||||
}
|
||||
|
||||
FILE *f = fopen(path.data(), "rb");
|
||||
if (!f) {
|
||||
return false;
|
||||
}
|
||||
buffer.resize(st.st_size);
|
||||
|
||||
if (fread(buffer.data(), 1, st.st_size, f) != st.st_size) {
|
||||
DEBUG_FUNCTION_LINE_WARN("Failed load %s", path.data());
|
||||
return false;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
return true;
|
||||
}
|
39
source/utils.h
Normal file
39
source/utils.h
Normal file
@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <malloc.h>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
template<class T, class... Args>
|
||||
std::unique_ptr<T> make_unique_nothrow(Args &&...args) noexcept(noexcept(T(std::forward<Args>(args)...))) {
|
||||
return std::unique_ptr<T>(new (std::nothrow) T(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline typename std::unique_ptr<T> make_unique_nothrow(size_t num) noexcept {
|
||||
return std::unique_ptr<T>(new (std::nothrow) std::remove_extent_t<T>[num]());
|
||||
}
|
||||
|
||||
template<class T, class... Args>
|
||||
std::shared_ptr<T> make_shared_nothrow(Args &&...args) noexcept(noexcept(T(std::forward<Args>(args)...))) {
|
||||
return std::shared_ptr<T>(new (std::nothrow) T(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
std::string string_format(const std::string &format, Args... args) {
|
||||
int size_s = std::snprintf(nullptr, 0, format.c_str(), args...) + 1; // Extra space for '\0'
|
||||
auto size = static_cast<size_t>(size_s);
|
||||
auto buf = std::make_unique<char[]>(size);
|
||||
std::snprintf(buf.get(), size, format.c_str(), args...);
|
||||
return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside
|
||||
}
|
||||
|
||||
bool GetTitleIdOfDisc(uint64_t *titleId, bool *discPresent);
|
||||
|
||||
bool DeleteMLCUpdateDirectory();
|
||||
|
||||
bool RestoreMLCUpdateDirectory();
|
||||
|
||||
bool LoadFileIntoBuffer(std::string_view path, std::vector<uint8_t> &buffer);
|
446
source/utils/ShaderSerializer.cpp
Normal file
446
source/utils/ShaderSerializer.cpp
Normal file
@ -0,0 +1,446 @@
|
||||
#include "ShaderSerializer.h"
|
||||
#include <cstdint>
|
||||
#include <gx2/sampler.h>
|
||||
#include <gx2/shaders.h>
|
||||
#include <gx2r/buffer.h>
|
||||
#include <malloc.h>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
/*
|
||||
* Based on https://github.com/Crementif/UntitledSandGame/blob/e752613ba54ac8f6767a8b37e9ac3f68ca180ad7/source/common/shader_serializer.h
|
||||
*/
|
||||
|
||||
template<typename Type>
|
||||
static void writeAt(std::vector<uint8_t> &fh, size_t pos, Type value) {
|
||||
*reinterpret_cast<Type *>(fh.data() + pos) = value;
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
static void write(std::vector<uint8_t> &fh, Type value) {
|
||||
auto pos = fh.size();
|
||||
fh.resize(pos + sizeof(Type));
|
||||
*reinterpret_cast<Type *>(fh.data() + pos) = value;
|
||||
}
|
||||
|
||||
static void writeString(std::vector<uint8_t> &fh, const char *str) {
|
||||
auto pos = fh.size();
|
||||
auto len = strlen(str) + 1;
|
||||
fh.resize((static_cast<size_t>(pos + len) + (4 - 1)) & ~(4 - 1));
|
||||
memcpy(fh.data() + pos, str, len);
|
||||
}
|
||||
|
||||
static void writeGX2RBuffer(std::vector<uint8_t> &fh, GX2RBuffer *buffer) {
|
||||
write(fh, buffer->flags);
|
||||
write(fh, buffer->elemSize);
|
||||
write(fh, buffer->elemCount);
|
||||
auto pos = fh.size();
|
||||
fh.resize(pos + buffer->elemSize * buffer->elemCount);
|
||||
memcpy(fh.data() + pos, buffer->buffer, buffer->elemSize * buffer->elemCount);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> SerializeVertexShader(GX2VertexShader *vertexShader) {
|
||||
std::vector<uint8_t> data;
|
||||
|
||||
// write regs
|
||||
write(data, vertexShader->regs.sq_pgm_resources_vs);
|
||||
write(data, vertexShader->regs.vgt_primitiveid_en);
|
||||
write(data, vertexShader->regs.spi_vs_out_config);
|
||||
write(data, vertexShader->regs.num_spi_vs_out_id);
|
||||
for (uint32_t spi_vs : vertexShader->regs.spi_vs_out_id) {
|
||||
write(data, spi_vs);
|
||||
}
|
||||
write(data, vertexShader->regs.pa_cl_vs_out_cntl);
|
||||
write(data, vertexShader->regs.sq_vtx_semantic_clear);
|
||||
write(data, vertexShader->regs.num_sq_vtx_semantic);
|
||||
for (uint32_t sq_vtx : vertexShader->regs.sq_vtx_semantic) {
|
||||
write(data, sq_vtx);
|
||||
}
|
||||
write(data, vertexShader->regs.vgt_strmout_buffer_en);
|
||||
write(data, vertexShader->regs.vgt_vertex_reuse_block_cntl);
|
||||
write(data, vertexShader->regs.vgt_hos_reuse_depth);
|
||||
|
||||
// write program
|
||||
write(data, vertexShader->size);
|
||||
for (uint32_t i = 0; i < vertexShader->size; i++) {
|
||||
write(data, ((uint8_t *) vertexShader->program)[i]);
|
||||
}
|
||||
write(data, vertexShader->mode);
|
||||
|
||||
// write uniform blocks
|
||||
write(data, vertexShader->uniformBlockCount);
|
||||
for (uint32_t i = 0; i < vertexShader->uniformBlockCount; i++) {
|
||||
writeString(data, vertexShader->uniformBlocks[i].name);
|
||||
write(data, vertexShader->uniformBlocks[i].offset);
|
||||
write(data, vertexShader->uniformBlocks[i].size);
|
||||
}
|
||||
|
||||
// write uniform vars
|
||||
write(data, vertexShader->uniformVarCount);
|
||||
for (uint32_t i = 0; i < vertexShader->uniformVarCount; i++) {
|
||||
writeString(data, vertexShader->uniformVars[i].name);
|
||||
write(data, vertexShader->uniformVars[i].type);
|
||||
write(data, vertexShader->uniformVars[i].count);
|
||||
write(data, vertexShader->uniformVars[i].offset);
|
||||
write(data, vertexShader->uniformVars[i].block);
|
||||
}
|
||||
|
||||
// write initial values
|
||||
write(data, vertexShader->initialValueCount);
|
||||
for (uint32_t i = 0; i < vertexShader->initialValueCount; i++) {
|
||||
write(data, vertexShader->initialValues[i].value[0]);
|
||||
write(data, vertexShader->initialValues[i].value[1]);
|
||||
write(data, vertexShader->initialValues[i].value[2]);
|
||||
write(data, vertexShader->initialValues[i].value[3]);
|
||||
write(data, vertexShader->initialValues[i].offset);
|
||||
}
|
||||
|
||||
// write loop vars
|
||||
write(data, vertexShader->loopVarCount);
|
||||
for (uint32_t i = 0; i < vertexShader->loopVarCount; i++) {
|
||||
write(data, vertexShader->loopVars[i].offset);
|
||||
write(data, vertexShader->loopVars[i].value);
|
||||
}
|
||||
|
||||
// write sampler vars
|
||||
write(data, vertexShader->samplerVarCount);
|
||||
for (uint32_t i = 0; i < vertexShader->samplerVarCount; i++) {
|
||||
writeString(data, vertexShader->samplerVars[i].name);
|
||||
write(data, vertexShader->samplerVars[i].type);
|
||||
write(data, vertexShader->samplerVars[i].location);
|
||||
}
|
||||
|
||||
// write attribute vars
|
||||
write(data, vertexShader->attribVarCount);
|
||||
for (uint32_t i = 0; i < vertexShader->attribVarCount; i++) {
|
||||
writeString(data, vertexShader->attribVars[i].name);
|
||||
write(data, vertexShader->attribVars[i].type);
|
||||
write(data, vertexShader->attribVars[i].count);
|
||||
write(data, vertexShader->attribVars[i].location);
|
||||
}
|
||||
|
||||
// write ring item size
|
||||
write(data, vertexShader->ringItemsize);
|
||||
|
||||
// write stream out
|
||||
write(data, vertexShader->hasStreamOut);
|
||||
for (uint32_t stride : vertexShader->streamOutStride) {
|
||||
write(data, stride);
|
||||
}
|
||||
|
||||
// write gx2rBuffer
|
||||
writeGX2RBuffer(data, &vertexShader->gx2rBuffer);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> SerializePixelShader(GX2PixelShader *pixelShader) {
|
||||
std::vector<uint8_t> data;
|
||||
|
||||
// write regs
|
||||
write(data, pixelShader->regs.sq_pgm_resources_ps);
|
||||
write(data, pixelShader->regs.sq_pgm_exports_ps);
|
||||
write(data, pixelShader->regs.spi_ps_in_control_0);
|
||||
write(data, pixelShader->regs.spi_ps_in_control_1);
|
||||
write(data, pixelShader->regs.num_spi_ps_input_cntl);
|
||||
for (uint32_t spi_ps : pixelShader->regs.spi_ps_input_cntls) {
|
||||
write(data, spi_ps);
|
||||
}
|
||||
write(data, pixelShader->regs.cb_shader_mask);
|
||||
write(data, pixelShader->regs.cb_shader_control);
|
||||
write(data, pixelShader->regs.db_shader_control);
|
||||
write(data, pixelShader->regs.spi_input_z);
|
||||
|
||||
// write program
|
||||
write(data, pixelShader->size);
|
||||
for (uint32_t i = 0; i < pixelShader->size; i++) {
|
||||
write(data, ((uint8_t *) pixelShader->program)[i]);
|
||||
}
|
||||
write(data, pixelShader->mode);
|
||||
|
||||
// write uniform blocks
|
||||
write(data, pixelShader->uniformBlockCount);
|
||||
for (uint32_t i = 0; i < pixelShader->uniformBlockCount; i++) {
|
||||
writeString(data, pixelShader->uniformBlocks[i].name);
|
||||
write(data, pixelShader->uniformBlocks[i].offset);
|
||||
write(data, pixelShader->uniformBlocks[i].size);
|
||||
}
|
||||
|
||||
// write uniform vars
|
||||
write(data, pixelShader->uniformVarCount);
|
||||
for (uint32_t i = 0; i < pixelShader->uniformVarCount; i++) {
|
||||
writeString(data, pixelShader->uniformVars[i].name);
|
||||
write(data, pixelShader->uniformVars[i].type);
|
||||
write(data, pixelShader->uniformVars[i].count);
|
||||
write(data, pixelShader->uniformVars[i].offset);
|
||||
write(data, pixelShader->uniformVars[i].block);
|
||||
}
|
||||
|
||||
// write initial values
|
||||
write(data, pixelShader->initialValueCount);
|
||||
for (uint32_t i = 0; i < pixelShader->initialValueCount; i++) {
|
||||
write(data, pixelShader->initialValues[i].value[0]);
|
||||
write(data, pixelShader->initialValues[i].value[1]);
|
||||
write(data, pixelShader->initialValues[i].value[2]);
|
||||
write(data, pixelShader->initialValues[i].value[3]);
|
||||
write(data, pixelShader->initialValues[i].offset);
|
||||
}
|
||||
|
||||
// write loop vars
|
||||
write(data, pixelShader->loopVarCount);
|
||||
for (uint32_t i = 0; i < pixelShader->loopVarCount; i++) {
|
||||
write(data, pixelShader->loopVars[i].offset);
|
||||
write(data, pixelShader->loopVars[i].value);
|
||||
}
|
||||
|
||||
// write sampler vars
|
||||
write(data, pixelShader->samplerVarCount);
|
||||
for (uint32_t i = 0; i < pixelShader->samplerVarCount; i++) {
|
||||
writeString(data, pixelShader->samplerVars[i].name);
|
||||
write(data, pixelShader->samplerVars[i].type);
|
||||
write(data, pixelShader->samplerVars[i].location);
|
||||
}
|
||||
|
||||
// write gx2rBuffer
|
||||
writeGX2RBuffer(data, &pixelShader->gx2rBuffer);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
static Type readAt(const std::span<const uint8_t> &data, size_t &pos) {
|
||||
Type value = *reinterpret_cast<const Type *>(data.data() + pos);
|
||||
pos += sizeof(Type);
|
||||
return value;
|
||||
}
|
||||
|
||||
static const char *readString(const std::span<const uint8_t> &data, size_t &pos) {
|
||||
std::string str(reinterpret_cast<const char *>(data.data() + pos));
|
||||
pos += str.size() + 1;
|
||||
pos = (pos + 3) & ~3; // align to 4 bytes
|
||||
|
||||
// Allocate memory for the string and copy the contents
|
||||
char *result = static_cast<char *>(malloc(sizeof(char) * (str.size() + 1)));
|
||||
strcpy(result, str.c_str());
|
||||
return result;
|
||||
}
|
||||
|
||||
static GX2RBuffer readGX2RBuffer(const std::span<const uint8_t> &data, size_t &pos) {
|
||||
GX2RBuffer buffer;
|
||||
buffer.flags = readAt<GX2RResourceFlags>(data, pos);
|
||||
buffer.elemSize = readAt<uint32_t>(data, pos);
|
||||
buffer.elemCount = readAt<uint32_t>(data, pos);
|
||||
size_t bufferSize = buffer.elemSize * buffer.elemCount;
|
||||
buffer.buffer = malloc(sizeof(uint8_t) * bufferSize);
|
||||
memcpy(buffer.buffer, data.data() + pos, bufferSize);
|
||||
pos += bufferSize;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
std::unique_ptr<GX2VertexShaderWrapper> DeserializeVertexShader(const std::span<const uint8_t> &data) {
|
||||
size_t pos = 0;
|
||||
auto vertexShaderWrapper = std::make_unique<GX2VertexShaderWrapper>();
|
||||
auto *vertexShader = vertexShaderWrapper->getVertexShader();
|
||||
*vertexShader = {};
|
||||
|
||||
// read regs
|
||||
vertexShader->regs.sq_pgm_resources_vs = readAt<uint32_t>(data, pos);
|
||||
vertexShader->regs.vgt_primitiveid_en = readAt<uint32_t>(data, pos);
|
||||
vertexShader->regs.spi_vs_out_config = readAt<uint32_t>(data, pos);
|
||||
vertexShader->regs.num_spi_vs_out_id = readAt<uint32_t>(data, pos);
|
||||
for (uint32_t &spi_vs : vertexShader->regs.spi_vs_out_id) {
|
||||
spi_vs = readAt<uint32_t>(data, pos);
|
||||
}
|
||||
vertexShader->regs.pa_cl_vs_out_cntl = readAt<uint32_t>(data, pos);
|
||||
vertexShader->regs.sq_vtx_semantic_clear = readAt<uint32_t>(data, pos);
|
||||
vertexShader->regs.num_sq_vtx_semantic = readAt<uint32_t>(data, pos);
|
||||
for (uint32_t &sq_vtx : vertexShader->regs.sq_vtx_semantic) {
|
||||
sq_vtx = readAt<uint32_t>(data, pos);
|
||||
}
|
||||
vertexShader->regs.vgt_strmout_buffer_en = readAt<uint32_t>(data, pos);
|
||||
vertexShader->regs.vgt_vertex_reuse_block_cntl = readAt<uint32_t>(data, pos);
|
||||
vertexShader->regs.vgt_hos_reuse_depth = readAt<uint32_t>(data, pos);
|
||||
|
||||
// read program
|
||||
vertexShader->size = readAt<uint32_t>(data, pos);
|
||||
vertexShader->program = memalign(256, vertexShader->size);
|
||||
for (uint32_t i = 0; i < vertexShader->size; i++) {
|
||||
static_cast<uint8_t *>(vertexShader->program)[i] = readAt<uint8_t>(data, pos);
|
||||
}
|
||||
vertexShader->mode = readAt<GX2ShaderMode>(data, pos);
|
||||
|
||||
// read uniform blocks
|
||||
vertexShader->uniformBlockCount = readAt<uint32_t>(data, pos);
|
||||
if (vertexShader->uniformBlockCount > 0) {
|
||||
vertexShader->uniformBlocks = static_cast<GX2UniformBlock *>(malloc(sizeof(GX2UniformBlock) * vertexShader->uniformBlockCount));
|
||||
for (uint32_t i = 0; i < vertexShader->uniformBlockCount; i++) {
|
||||
vertexShader->uniformBlocks[i].name = readString(data, pos);
|
||||
vertexShader->uniformBlocks[i].offset = readAt<uint32_t>(data, pos);
|
||||
vertexShader->uniformBlocks[i].size = readAt<uint32_t>(data, pos);
|
||||
}
|
||||
}
|
||||
|
||||
// read uniform vars
|
||||
vertexShader->uniformVarCount = readAt<uint32_t>(data, pos);
|
||||
if (vertexShader->uniformVarCount > 0) {
|
||||
vertexShader->uniformVars = static_cast<GX2UniformVar *>(malloc(sizeof(GX2UniformVar) * vertexShader->uniformVarCount));
|
||||
|
||||
for (uint32_t i = 0; i < vertexShader->uniformVarCount; i++) {
|
||||
vertexShader->uniformVars[i].name = readString(data, pos);
|
||||
vertexShader->uniformVars[i].type = readAt<GX2ShaderVarType>(data, pos);
|
||||
vertexShader->uniformVars[i].count = readAt<uint32_t>(data, pos);
|
||||
vertexShader->uniformVars[i].offset = readAt<uint32_t>(data, pos);
|
||||
vertexShader->uniformVars[i].block = readAt<int32_t>(data, pos);
|
||||
}
|
||||
}
|
||||
|
||||
// read initial values
|
||||
vertexShader->initialValueCount = readAt<uint32_t>(data, pos);
|
||||
if (vertexShader->initialValueCount > 0) {
|
||||
vertexShader->initialValues = static_cast<GX2UniformInitialValue *>(malloc(sizeof(GX2UniformInitialValue) * vertexShader->initialValueCount));
|
||||
for (uint32_t i = 0; i < vertexShader->initialValueCount; i++) {
|
||||
vertexShader->initialValues[i].value[0] = readAt<float>(data, pos);
|
||||
vertexShader->initialValues[i].value[1] = readAt<float>(data, pos);
|
||||
vertexShader->initialValues[i].value[2] = readAt<float>(data, pos);
|
||||
vertexShader->initialValues[i].value[3] = readAt<float>(data, pos);
|
||||
vertexShader->initialValues[i].offset = readAt<uint32_t>(data, pos);
|
||||
}
|
||||
}
|
||||
|
||||
// read loop vars
|
||||
vertexShader->loopVarCount = readAt<uint32_t>(data, pos);
|
||||
if (vertexShader->loopVarCount > 0) {
|
||||
vertexShader->loopVars = static_cast<GX2LoopVar *>(malloc(sizeof(GX2LoopVar) + vertexShader->loopVarCount));
|
||||
for (uint32_t i = 0; i < vertexShader->loopVarCount; i++) {
|
||||
vertexShader->loopVars[i].offset = readAt<uint32_t>(data, pos);
|
||||
vertexShader->loopVars[i].value = readAt<uint32_t>(data, pos);
|
||||
}
|
||||
}
|
||||
|
||||
// read sampler vars
|
||||
vertexShader->samplerVarCount = readAt<uint32_t>(data, pos);
|
||||
if (vertexShader->samplerVarCount > 0) {
|
||||
vertexShader->samplerVars = static_cast<GX2SamplerVar *>(malloc(sizeof(GX2SamplerVar) * vertexShader->samplerVarCount));
|
||||
for (uint32_t i = 0; i < vertexShader->samplerVarCount; i++) {
|
||||
vertexShader->samplerVars[i].name = readString(data, pos);
|
||||
vertexShader->samplerVars[i].type = readAt<GX2SamplerVarType>(data, pos);
|
||||
vertexShader->samplerVars[i].location = readAt<uint32_t>(data, pos);
|
||||
}
|
||||
}
|
||||
|
||||
// read attribute vars
|
||||
vertexShader->attribVarCount = readAt<uint32_t>(data, pos);
|
||||
if (vertexShader->attribVarCount > 0) {
|
||||
vertexShader->attribVars = static_cast<GX2AttribVar *>(malloc(sizeof(GX2AttribVar) * vertexShader->attribVarCount));
|
||||
for (uint32_t i = 0; i < vertexShader->attribVarCount; i++) {
|
||||
vertexShader->attribVars[i].name = readString(data, pos);
|
||||
vertexShader->attribVars[i].type = readAt<GX2ShaderVarType>(data, pos);
|
||||
vertexShader->attribVars[i].count = readAt<uint32_t>(data, pos);
|
||||
vertexShader->attribVars[i].location = readAt<uint32_t>(data, pos);
|
||||
}
|
||||
}
|
||||
|
||||
// read ring item size
|
||||
vertexShader->ringItemsize = readAt<uint32_t>(data, pos);
|
||||
|
||||
// read stream out
|
||||
vertexShader->hasStreamOut = readAt<BOOL>(data, pos);
|
||||
for (uint32_t &stride : vertexShader->streamOutStride) {
|
||||
stride = readAt<uint32_t>(data, pos);
|
||||
}
|
||||
|
||||
// read gx2rBuffer
|
||||
vertexShader->gx2rBuffer = readGX2RBuffer(data, pos);
|
||||
|
||||
return vertexShaderWrapper;
|
||||
}
|
||||
|
||||
std::unique_ptr<GX2PixelShaderWrapper> DeserializePixelShader(const std::span<const uint8_t> &data) {
|
||||
size_t pos = 0;
|
||||
auto pixelShaderWrapper = std::make_unique<GX2PixelShaderWrapper>();
|
||||
auto *pixelShader = pixelShaderWrapper->getPixelShader();
|
||||
*pixelShader = {};
|
||||
// read regs
|
||||
pixelShader->regs.sq_pgm_resources_ps = readAt<uint32_t>(data, pos);
|
||||
pixelShader->regs.sq_pgm_exports_ps = readAt<uint32_t>(data, pos);
|
||||
pixelShader->regs.spi_ps_in_control_0 = readAt<uint32_t>(data, pos);
|
||||
pixelShader->regs.spi_ps_in_control_1 = readAt<uint32_t>(data, pos);
|
||||
pixelShader->regs.num_spi_ps_input_cntl = readAt<uint32_t>(data, pos);
|
||||
for (uint32_t &spi_ps : pixelShader->regs.spi_ps_input_cntls) {
|
||||
spi_ps = readAt<uint32_t>(data, pos);
|
||||
}
|
||||
pixelShader->regs.cb_shader_mask = readAt<uint32_t>(data, pos);
|
||||
pixelShader->regs.cb_shader_control = readAt<uint32_t>(data, pos);
|
||||
pixelShader->regs.db_shader_control = readAt<uint32_t>(data, pos);
|
||||
pixelShader->regs.spi_input_z = readAt<uint32_t>(data, pos);
|
||||
|
||||
// read program
|
||||
pixelShader->size = readAt<uint32_t>(data, pos);
|
||||
pixelShader->program = memalign(256, pixelShader->size);
|
||||
for (uint32_t i = 0; i < pixelShader->size; i++) {
|
||||
((uint8_t *) pixelShader->program)[i] = readAt<uint8_t>(data, pos);
|
||||
}
|
||||
pixelShader->mode = readAt<GX2ShaderMode>(data, pos);
|
||||
|
||||
// read uniform blocks
|
||||
pixelShader->uniformBlockCount = readAt<uint32_t>(data, pos);
|
||||
if (pixelShader->uniformBlockCount > 0) {
|
||||
pixelShader->uniformBlocks = static_cast<GX2UniformBlock *>(malloc(sizeof(GX2UniformBlock) * pixelShader->uniformBlockCount));
|
||||
for (uint32_t i = 0; i < pixelShader->uniformBlockCount; i++) {
|
||||
pixelShader->uniformBlocks[i].name = readString(data, pos);
|
||||
pixelShader->uniformBlocks[i].offset = readAt<uint32_t>(data, pos);
|
||||
pixelShader->uniformBlocks[i].size = readAt<uint32_t>(data, pos);
|
||||
}
|
||||
}
|
||||
|
||||
// read uniform vars
|
||||
pixelShader->uniformVarCount = readAt<uint32_t>(data, pos);
|
||||
if (pixelShader->uniformVarCount > 0) {
|
||||
pixelShader->uniformVars = static_cast<GX2UniformVar *>(malloc(sizeof(GX2UniformVar) + pixelShader->uniformVarCount));
|
||||
for (uint32_t i = 0; i < pixelShader->uniformVarCount; i++) {
|
||||
pixelShader->uniformVars[i].name = readString(data, pos);
|
||||
pixelShader->uniformVars[i].type = readAt<GX2ShaderVarType>(data, pos);
|
||||
pixelShader->uniformVars[i].count = readAt<uint32_t>(data, pos);
|
||||
pixelShader->uniformVars[i].offset = readAt<uint32_t>(data, pos);
|
||||
pixelShader->uniformVars[i].block = readAt<int32_t>(data, pos);
|
||||
}
|
||||
}
|
||||
|
||||
// read initial values
|
||||
pixelShader->initialValueCount = readAt<uint32_t>(data, pos);
|
||||
if (pixelShader->initialValueCount > 0) {
|
||||
pixelShader->initialValues = static_cast<GX2UniformInitialValue *>(malloc(sizeof(GX2UniformInitialValue) * pixelShader->initialValueCount));
|
||||
for (uint32_t i = 0; i < pixelShader->initialValueCount; i++) {
|
||||
pixelShader->initialValues[i].value[0] = readAt<float>(data, pos);
|
||||
pixelShader->initialValues[i].value[1] = readAt<float>(data, pos);
|
||||
pixelShader->initialValues[i].value[2] = readAt<float>(data, pos);
|
||||
pixelShader->initialValues[i].value[3] = readAt<float>(data, pos);
|
||||
pixelShader->initialValues[i].offset = readAt<uint32_t>(data, pos);
|
||||
}
|
||||
}
|
||||
|
||||
pixelShader->loopVarCount = readAt<uint32_t>(data, pos);
|
||||
if (pixelShader->loopVarCount > 0) {
|
||||
pixelShader->loopVars = static_cast<GX2LoopVar *>(malloc(sizeof(GX2LoopVar) * pixelShader->loopVarCount));
|
||||
for (uint32_t i = 0; i < pixelShader->loopVarCount; i++) {
|
||||
pixelShader->loopVars[i].offset = readAt<uint32_t>(data, pos);
|
||||
pixelShader->loopVars[i].value = readAt<uint32_t>(data, pos);
|
||||
}
|
||||
}
|
||||
|
||||
pixelShader->samplerVarCount = readAt<uint32_t>(data, pos);
|
||||
if (pixelShader->samplerVarCount > 0) {
|
||||
pixelShader->samplerVars = static_cast<GX2SamplerVar *>(malloc(sizeof(GX2SamplerVar) * pixelShader->samplerVarCount));
|
||||
for (uint32_t i = 0; i < pixelShader->samplerVarCount; i++) {
|
||||
pixelShader->samplerVars[i].name = readString(data, pos);
|
||||
pixelShader->samplerVars[i].type = readAt<GX2SamplerVarType>(data, pos);
|
||||
pixelShader->samplerVars[i].location = readAt<uint32_t>(data, pos);
|
||||
}
|
||||
}
|
||||
|
||||
pixelShader->gx2rBuffer = readGX2RBuffer(data, pos);
|
||||
|
||||
return pixelShaderWrapper;
|
||||
}
|
120
source/utils/ShaderSerializer.h
Normal file
120
source/utils/ShaderSerializer.h
Normal file
@ -0,0 +1,120 @@
|
||||
#pragma once
|
||||
|
||||
#include <gx2/shaders.h>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
class GX2PixelShaderWrapper {
|
||||
public:
|
||||
[[nodiscard]] GX2PixelShader *getPixelShader() {
|
||||
return &pixelShader;
|
||||
}
|
||||
|
||||
~GX2PixelShaderWrapper() {
|
||||
if (pixelShader.program) {
|
||||
free(pixelShader.program);
|
||||
}
|
||||
|
||||
if (pixelShader.uniformBlocks) {
|
||||
for (uint32_t i = 0; i < pixelShader.uniformBlockCount; i++) {
|
||||
free((void *) pixelShader.uniformBlocks[i].name);
|
||||
}
|
||||
|
||||
free(pixelShader.uniformBlocks);
|
||||
}
|
||||
|
||||
if (pixelShader.uniformVars) {
|
||||
for (uint32_t i = 0; i < pixelShader.uniformVarCount; i++) {
|
||||
free((void *) pixelShader.uniformVars[i].name);
|
||||
}
|
||||
free(pixelShader.uniformVars);
|
||||
}
|
||||
|
||||
if (pixelShader.initialValues) {
|
||||
free(pixelShader.initialValues);
|
||||
}
|
||||
|
||||
if (pixelShader.samplerVars) {
|
||||
for (uint32_t i = 0; i < pixelShader.samplerVarCount; i++) {
|
||||
free((void *) pixelShader.samplerVars[i].name);
|
||||
}
|
||||
free(pixelShader.samplerVars);
|
||||
}
|
||||
|
||||
if (pixelShader.loopVars) {
|
||||
free(pixelShader.loopVars);
|
||||
}
|
||||
|
||||
if (pixelShader.gx2rBuffer.buffer) {
|
||||
free(pixelShader.gx2rBuffer.buffer);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
alignas(0x40) GX2PixelShader pixelShader;
|
||||
};
|
||||
|
||||
class GX2VertexShaderWrapper {
|
||||
public:
|
||||
[[nodiscard]] GX2VertexShader *getVertexShader() {
|
||||
return &vertexShader;
|
||||
}
|
||||
|
||||
~GX2VertexShaderWrapper() {
|
||||
if (vertexShader.program) {
|
||||
free(vertexShader.program);
|
||||
}
|
||||
|
||||
if (vertexShader.uniformBlocks) {
|
||||
for (uint32_t i = 0; i < vertexShader.uniformBlockCount; i++) {
|
||||
free((void *) vertexShader.uniformBlocks[i].name);
|
||||
}
|
||||
|
||||
free(vertexShader.uniformBlocks);
|
||||
}
|
||||
|
||||
if (vertexShader.uniformVars) {
|
||||
for (uint32_t i = 0; i < vertexShader.uniformVarCount; i++) {
|
||||
free((void *) vertexShader.uniformVars[i].name);
|
||||
}
|
||||
|
||||
free(vertexShader.uniformVars);
|
||||
}
|
||||
|
||||
if (vertexShader.initialValues) {
|
||||
free(vertexShader.initialValues);
|
||||
}
|
||||
|
||||
if (vertexShader.loopVars) {
|
||||
free(vertexShader.loopVars);
|
||||
}
|
||||
|
||||
if (vertexShader.samplerVars) {
|
||||
for (uint32_t i = 0; i < vertexShader.samplerVarCount; i++) {
|
||||
free((void *) vertexShader.samplerVars[i].name);
|
||||
}
|
||||
|
||||
free(vertexShader.samplerVars);
|
||||
}
|
||||
|
||||
if (vertexShader.attribVars) {
|
||||
for (uint32_t i = 0; i < vertexShader.attribVarCount; i++) {
|
||||
free((void *) vertexShader.attribVars[i].name);
|
||||
}
|
||||
|
||||
free(vertexShader.attribVars);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
alignas(0x40) GX2VertexShader vertexShader;
|
||||
};
|
||||
|
||||
std::vector<uint8_t> SerializeVertexShader(GX2VertexShader *vertexShader);
|
||||
|
||||
std::vector<uint8_t> SerializePixelShader(GX2PixelShader *pixelShader);
|
||||
|
||||
std::unique_ptr<GX2VertexShaderWrapper> DeserializeVertexShader(const std::span<const uint8_t> &data);
|
||||
|
||||
std::unique_ptr<GX2PixelShaderWrapper> DeserializePixelShader(const std::span<const uint8_t> &data);
|
220
source/utils/SplashScreenDrawer.cpp
Normal file
220
source/utils/SplashScreenDrawer.cpp
Normal file
@ -0,0 +1,220 @@
|
||||
#include "SplashScreenDrawer.h"
|
||||
#include "ShaderSerializer.h"
|
||||
#include "TGATexture.h"
|
||||
#include "gfx.h"
|
||||
#include "logger.h"
|
||||
#include "utils.h"
|
||||
#include <fstream>
|
||||
#include <gx2/draw.h>
|
||||
#include <gx2/mem.h>
|
||||
#include <gx2r/draw.h>
|
||||
#include <whb/log.h>
|
||||
|
||||
/*
|
||||
constexpr const char *s_textureVertexShader = R"(
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec2 aPos;
|
||||
layout(location = 1) in vec2 aTexCoord;
|
||||
|
||||
layout(location = 0) out vec2 TexCoord;
|
||||
|
||||
void main()
|
||||
{
|
||||
TexCoord = aTexCoord;
|
||||
gl_Position = vec4(aPos.x, aPos.y, 0.0f, 1.0f);
|
||||
}
|
||||
)";
|
||||
*/
|
||||
|
||||
constexpr uint8_t s_textureVertexShaderCompiled[] = {
|
||||
0x00, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0x8A, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF,
|
||||
0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF,
|
||||
0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF,
|
||||
0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF,
|
||||
0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF,
|
||||
0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF,
|
||||
0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF,
|
||||
0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF,
|
||||
0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF,
|
||||
0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF,
|
||||
0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E,
|
||||
0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x80, 0x89, 0x00, 0x40, 0x01, 0xC0, 0xC8, 0x0F, 0x00, 0x94,
|
||||
0x3C, 0xA0, 0x00, 0xC0, 0x08, 0x0B, 0x00, 0x94, 0x05, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x80,
|
||||
0x00, 0x00, 0x00, 0x80, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0F, 0xFF,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x61, 0x50, 0x6F, 0x73,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x61, 0x54, 0x65, 0x78, 0x43, 0x6F, 0x6F, 0x72,
|
||||
0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
/*
|
||||
constexpr const char *s_texturePixelShader = R"(
|
||||
#version 450
|
||||
#extension GL_ARB_shading_language_420pack: enable
|
||||
|
||||
layout(location = 0) in vec2 TexCoord;
|
||||
|
||||
layout(location = 0) out vec4 FragColor;
|
||||
|
||||
layout(binding = 0) uniform sampler2D inTexture;
|
||||
|
||||
void main()
|
||||
{
|
||||
FragColor = texture(inTexture, TexCoord);
|
||||
}
|
||||
)";*/
|
||||
constexpr uint8_t s_texturePixelShaderCompiled[] = {
|
||||
0x00, 0x20, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x8A,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
|
||||
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0xC0,
|
||||
0x88, 0x06, 0x20, 0x94, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x0D, 0xF0,
|
||||
0x00, 0x00, 0x80, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x0F, 0xFF,
|
||||
0x00, 0x00, 0x00, 0x01, 0x69, 0x6E, 0x54, 0x65, 0x78, 0x74, 0x75, 0x72,
|
||||
0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
static GX2Texture *LoadTGAAsTexture(std::string_view path) {
|
||||
std::vector<uint8_t> buffer;
|
||||
if (!LoadFileIntoBuffer(path, buffer)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto *texture = TGA_LoadTexture(buffer);
|
||||
if (texture == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return texture;
|
||||
}
|
||||
|
||||
SplashScreenDrawer::SplashScreenDrawer(std::string_view meta_dir) {
|
||||
// create shader group
|
||||
mVertexShaderWrapper = DeserializeVertexShader(s_textureVertexShaderCompiled);
|
||||
mPixelShaderWrapper = DeserializePixelShader(s_texturePixelShaderCompiled);
|
||||
|
||||
mShaderGroup = {};
|
||||
mShaderGroup.vertexShader = mVertexShaderWrapper->getVertexShader();
|
||||
mShaderGroup.pixelShader = mPixelShaderWrapper->getPixelShader();
|
||||
|
||||
GX2Invalidate(GX2_INVALIDATE_MODE_CPU_SHADER, mShaderGroup.vertexShader->program, mShaderGroup.vertexShader->size);
|
||||
GX2Invalidate(GX2_INVALIDATE_MODE_CPU_SHADER, mShaderGroup.pixelShader->program, mShaderGroup.pixelShader->size);
|
||||
|
||||
GX2SetShaderMode(GX2_SHADER_MODE_UNIFORM_BLOCK);
|
||||
|
||||
GfxInitShaderAttribute(&mShaderGroup, "aPos", 0, 0, GX2_ATTRIB_FORMAT_FLOAT_32_32);
|
||||
GfxInitShaderAttribute(&mShaderGroup, "aTexCoord", 1, 0, GX2_ATTRIB_FORMAT_FLOAT_32_32);
|
||||
GfxInitFetchShader(&mShaderGroup);
|
||||
|
||||
// upload vertex position
|
||||
mPositionBuffer.flags = GX2R_RESOURCE_BIND_VERTEX_BUFFER | GX2R_RESOURCE_USAGE_CPU_READ | GX2R_RESOURCE_USAGE_CPU_WRITE | GX2R_RESOURCE_USAGE_GPU_READ;
|
||||
mPositionBuffer.elemSize = 2 * sizeof(float);
|
||||
mPositionBuffer.elemCount = 4;
|
||||
GX2RCreateBuffer(&mPositionBuffer);
|
||||
void *posUploadBuffer = GX2RLockBufferEx(&mPositionBuffer, GX2R_RESOURCE_BIND_NONE);
|
||||
memcpy(posUploadBuffer, mPositionData, mPositionBuffer.elemSize * mPositionBuffer.elemCount);
|
||||
GX2RUnlockBufferEx(&mPositionBuffer, GX2R_RESOURCE_BIND_NONE);
|
||||
|
||||
// upload texture coords
|
||||
mTexCoordBuffer.flags = GX2R_RESOURCE_BIND_VERTEX_BUFFER | GX2R_RESOURCE_USAGE_CPU_READ | GX2R_RESOURCE_USAGE_CPU_WRITE | GX2R_RESOURCE_USAGE_GPU_READ;
|
||||
mTexCoordBuffer.elemSize = 2 * sizeof(float);
|
||||
mTexCoordBuffer.elemCount = 4;
|
||||
GX2RCreateBuffer(&mTexCoordBuffer);
|
||||
void *coordsUploadBuffer = GX2RLockBufferEx(&mTexCoordBuffer, GX2R_RESOURCE_BIND_NONE);
|
||||
memcpy(coordsUploadBuffer, mTexCoords, mTexCoordBuffer.elemSize * mTexCoordBuffer.elemCount);
|
||||
GX2RUnlockBufferEx(&mTexCoordBuffer, GX2R_RESOURCE_BIND_NONE);
|
||||
|
||||
std::string bootTvTex = std::string(meta_dir).append("/bootTvTex.tga");
|
||||
std::string bootDrcTex = std::string(meta_dir).append("/bootDrcTex.tga");
|
||||
mTextureTV = LoadTGAAsTexture(bootTvTex);
|
||||
mTextureDRC = LoadTGAAsTexture(bootDrcTex);
|
||||
|
||||
GX2Sampler sampler;
|
||||
GX2InitSampler(&sampler, GX2_TEX_CLAMP_MODE_CLAMP, GX2_TEX_XY_FILTER_MODE_LINEAR);
|
||||
}
|
||||
|
||||
void SplashScreenDrawer::Draw() {
|
||||
if (!mTextureTV || !mTextureDRC) {
|
||||
DEBUG_FUNCTION_LINE_ERR("Textures are missing");
|
||||
return;
|
||||
}
|
||||
|
||||
GfxBeginRender();
|
||||
|
||||
GfxBeginRenderTV();
|
||||
GX2SetFetchShader(&mShaderGroup.fetchShader);
|
||||
GX2SetVertexShader(mShaderGroup.vertexShader);
|
||||
GX2SetPixelShader(mShaderGroup.pixelShader);
|
||||
GX2SetShaderMode(GX2_SHADER_MODE_UNIFORM_BLOCK);
|
||||
|
||||
GX2RSetAttributeBuffer(&mPositionBuffer, 0, mPositionBuffer.elemSize, 0);
|
||||
GX2RSetAttributeBuffer(&mTexCoordBuffer, 1, mTexCoordBuffer.elemSize, 0);
|
||||
GX2SetPixelTexture(mTextureTV, mShaderGroup.pixelShader->samplerVars[0].location);
|
||||
GX2SetPixelSampler(&mSampler, mShaderGroup.pixelShader->samplerVars[0].location);
|
||||
|
||||
GX2DrawEx(GX2_PRIMITIVE_MODE_QUADS, 4, 0, 1);
|
||||
GfxFinishRenderTV();
|
||||
|
||||
GfxBeginRenderDRC();
|
||||
GX2SetFetchShader(&mShaderGroup.fetchShader);
|
||||
GX2SetVertexShader(mShaderGroup.vertexShader);
|
||||
GX2SetPixelShader(mShaderGroup.pixelShader);
|
||||
GX2SetShaderMode(GX2_SHADER_MODE_UNIFORM_BLOCK);
|
||||
|
||||
GX2RSetAttributeBuffer(&mPositionBuffer, 0, mPositionBuffer.elemSize, 0);
|
||||
GX2RSetAttributeBuffer(&mTexCoordBuffer, 1, mTexCoordBuffer.elemSize, 0);
|
||||
GX2SetPixelTexture(mTextureDRC, mShaderGroup.pixelShader->samplerVars[0].location);
|
||||
GX2SetPixelSampler(&mSampler, mShaderGroup.pixelShader->samplerVars[0].location);
|
||||
|
||||
GX2DrawEx(GX2_PRIMITIVE_MODE_QUADS, 4, 0, 1);
|
||||
GfxFinishRenderDRC();
|
||||
|
||||
GfxFinishRender();
|
||||
}
|
||||
|
||||
SplashScreenDrawer::~SplashScreenDrawer() {
|
||||
GX2RDestroyBufferEx(&mPositionBuffer, GX2R_RESOURCE_BIND_NONE);
|
||||
GX2RDestroyBufferEx(&mTexCoordBuffer, GX2R_RESOURCE_BIND_NONE);
|
||||
if (mTextureTV) {
|
||||
if (mTextureTV->surface.image != nullptr) {
|
||||
free(mTextureTV->surface.image);
|
||||
mTextureTV->surface.image = nullptr;
|
||||
}
|
||||
::free(mTextureTV);
|
||||
mTextureTV = nullptr;
|
||||
}
|
||||
if (mTextureDRC) {
|
||||
if (mTextureDRC->surface.image != nullptr) {
|
||||
free(mTextureDRC->surface.image);
|
||||
mTextureDRC->surface.image = nullptr;
|
||||
}
|
||||
::free(mTextureDRC);
|
||||
mTextureDRC = nullptr;
|
||||
}
|
||||
}
|
50
source/utils/SplashScreenDrawer.h
Normal file
50
source/utils/SplashScreenDrawer.h
Normal file
@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include "ShaderSerializer.h"
|
||||
#include "gfx.h"
|
||||
#include <gx2/sampler.h>
|
||||
#include <gx2/shaders.h>
|
||||
#include <gx2/texture.h>
|
||||
#include <gx2r/buffer.h>
|
||||
#include <memory>
|
||||
|
||||
class SplashScreenDrawer {
|
||||
public:
|
||||
explicit SplashScreenDrawer(std::string_view meta_dir);
|
||||
|
||||
void Draw();
|
||||
|
||||
virtual ~SplashScreenDrawer();
|
||||
|
||||
private:
|
||||
const float mPositionData[8] = {
|
||||
-1.0f,
|
||||
-1.0f,
|
||||
1.0f,
|
||||
-1.0f,
|
||||
1.0f,
|
||||
1.0f,
|
||||
-1.0f,
|
||||
1.0f,
|
||||
};
|
||||
|
||||
const float mTexCoords[8] = {
|
||||
0.0f,
|
||||
1.0f,
|
||||
1.0f,
|
||||
1.0f,
|
||||
1.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
};
|
||||
|
||||
WHBGfxShaderGroup mShaderGroup = {};
|
||||
std::unique_ptr<GX2VertexShaderWrapper> mVertexShaderWrapper;
|
||||
std::unique_ptr<GX2PixelShaderWrapper> mPixelShaderWrapper;
|
||||
GX2RBuffer mPositionBuffer = {};
|
||||
GX2RBuffer mTexCoordBuffer = {};
|
||||
GX2Texture *mTextureTV = nullptr;
|
||||
GX2Texture *mTextureDRC = nullptr;
|
||||
GX2Sampler mSampler = {};
|
||||
};
|
102
source/utils/SplashSoundPlayer.cpp
Normal file
102
source/utils/SplashSoundPlayer.cpp
Normal file
@ -0,0 +1,102 @@
|
||||
#include "SplashSoundPlayer.h"
|
||||
#include "logger.h"
|
||||
#include "utils.h"
|
||||
#include <coreinit/transition.h>
|
||||
#include <cstring>
|
||||
#include <sndcore2/core.h>
|
||||
#include <sndcore2/device.h>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <sys/stat.h>
|
||||
#include <vector>
|
||||
#include <whb/log.h>
|
||||
|
||||
SplashSoundPlayer::SplashSoundPlayer(std::string_view meta_dir) {
|
||||
std::string bootSound = std::string(meta_dir).append("/bootSound.btsnd");
|
||||
|
||||
if (!LoadFileIntoBuffer(bootSound, mBuffer)) {
|
||||
mBuffer.clear();
|
||||
return;
|
||||
}
|
||||
if (mBuffer.size() < 8) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto target = ((uint32_t *) mBuffer.data())[0];
|
||||
if (target <= 2) {
|
||||
mOutputTarget = static_cast<TransitionAudioTarget>(target);
|
||||
}
|
||||
|
||||
mLoopPoint = ((uint32_t *) mBuffer.data())[1];
|
||||
}
|
||||
|
||||
void SplashSoundPlayer::Play() {
|
||||
if (mBuffer.empty() || mBuffer.size() < 8) {
|
||||
DEBUG_FUNCTION_LINE_WARN("mBuffer is empty or too small");
|
||||
return;
|
||||
}
|
||||
AXTransitionAudioBuffer transitionAudioBuffer = {};
|
||||
|
||||
AXInit();
|
||||
AXDeviceMode tvMode;
|
||||
AXDeviceMode drcMode;
|
||||
AXGetDeviceMode(0, &tvMode);
|
||||
AXGetDeviceMode(1, &drcMode);
|
||||
AXQuit();
|
||||
|
||||
WHBLogPrintf("TV mode before transition = %d", tvMode);
|
||||
transitionAudioBuffer.tv.mode = tvMode;
|
||||
|
||||
WHBLogPrintf("DRC mode before transition = %d", drcMode);
|
||||
transitionAudioBuffer.drc.mode = drcMode;
|
||||
|
||||
transitionAudioBuffer.unk1 = 1;
|
||||
transitionAudioBuffer.unk2 = 0;
|
||||
|
||||
transitionAudioBuffer.tv.unk1 = 0.99987207655f;
|
||||
transitionAudioBuffer.tv.unk2 = 600;
|
||||
|
||||
transitionAudioBuffer.drc.unk1 = transitionAudioBuffer.tv.unk1;
|
||||
transitionAudioBuffer.tv.unk2 = transitionAudioBuffer.tv.unk2;
|
||||
|
||||
switch (mOutputTarget) {
|
||||
case TV_ONLY:
|
||||
transitionAudioBuffer.tv.enabled = true;
|
||||
transitionAudioBuffer.drc.enabled = false;
|
||||
break;
|
||||
case DRC_ONLY:
|
||||
transitionAudioBuffer.tv.enabled = false;
|
||||
transitionAudioBuffer.drc.enabled = true;
|
||||
break;
|
||||
case BOTH:
|
||||
transitionAudioBuffer.tv.enabled = true;
|
||||
transitionAudioBuffer.drc.enabled = true;
|
||||
break;
|
||||
}
|
||||
|
||||
void *audioBuffer = (void *) nullptr;
|
||||
uint32_t audioBufferLen = 0;
|
||||
auto res = __OSGetTransitionAudioBuffer(&audioBuffer, &audioBufferLen);
|
||||
if (res == 0) {
|
||||
DEBUG_FUNCTION_LINE_ERR("Could not get access to audio buffer from foreground bucket");
|
||||
return;
|
||||
}
|
||||
|
||||
std::span<uint8_t> audioBufferIn(mBuffer.data() + 8, mBuffer.size() - 8);
|
||||
|
||||
DEBUG_FUNCTION_LINE("Got audio buffer from foreground bucket @ %8.8x len = %d", audioBuffer, audioBufferLen);
|
||||
if (audioBufferLen < mBuffer.size()) {
|
||||
DEBUG_FUNCTION_LINE_ERR("buffer not big enough");
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(audioBuffer, audioBufferIn.data(), audioBufferIn.size());
|
||||
__OSSetTransitionAudioSize(audioBufferIn.size());
|
||||
|
||||
transitionAudioBuffer.length = audioBufferIn.size();
|
||||
transitionAudioBuffer.loopPoint = mLoopPoint;
|
||||
transitionAudioBuffer.audioBuffer = audioBuffer;
|
||||
transitionAudioBuffer.audioBufferLen = audioBufferLen;
|
||||
AXSetUpTransitionAudio((AXTransitionAudioBuffer *) &transitionAudioBuffer);
|
||||
AXStartTransitionAudio();
|
||||
}
|
23
source/utils/SplashSoundPlayer.h
Normal file
23
source/utils/SplashSoundPlayer.h
Normal file
@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
class SplashSoundPlayer {
|
||||
public:
|
||||
SplashSoundPlayer(std::string_view meta_dir);
|
||||
|
||||
void Play();
|
||||
|
||||
virtual ~SplashSoundPlayer() = default;
|
||||
|
||||
private:
|
||||
enum TransitionAudioTarget {
|
||||
TV_ONLY,
|
||||
DRC_ONLY,
|
||||
BOTH
|
||||
};
|
||||
std::vector<uint8_t> mBuffer;
|
||||
TransitionAudioTarget mOutputTarget = BOTH;
|
||||
uint32_t mLoopPoint = 0;
|
||||
};
|
84
source/utils/TGATexture.cpp
Normal file
84
source/utils/TGATexture.cpp
Normal file
@ -0,0 +1,84 @@
|
||||
#include <gx2/draw.h>
|
||||
#include <gx2/mem.h>
|
||||
#include <malloc.h>
|
||||
#include <span>
|
||||
|
||||
#include "TGATexture.h"
|
||||
#include "logger.h"
|
||||
#include <whb/log.h>
|
||||
|
||||
/*
|
||||
* Based on
|
||||
* https://github.com/Xpl0itU/savemii/blob/70e3b63db52113519230e1e39bd56876cef12dc8/src/tga_reader.cpp
|
||||
* and
|
||||
* https://github.com/Crementif/WiiU-GX2-Shader-Examples/blob/5a88f861043dcb7666d4d25a6bab6bd271e76d5f/include/TGATexture.h
|
||||
*/
|
||||
|
||||
uint16_t inline _swapU16(uint16_t v) {
|
||||
return (v >> 8) | (v << 8);
|
||||
}
|
||||
|
||||
GX2Texture *TGA_LoadTexture(std::span<uint8_t> data) {
|
||||
TGA_HEADER *tgaHeader = (TGA_HEADER *) data.data();
|
||||
|
||||
uint32_t width = _swapU16(tgaHeader->width);
|
||||
uint32_t height = _swapU16(tgaHeader->height);
|
||||
|
||||
if (tgaHeader->bits != 24) {
|
||||
DEBUG_FUNCTION_LINE_WARN("Only 24bit TGA images are supported");
|
||||
return nullptr;
|
||||
}
|
||||
if (tgaHeader->imagetype != 2 && tgaHeader->imagetype != 3) {
|
||||
DEBUG_FUNCTION_LINE_WARN("Only uncompressed TGA images are supported");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GX2Texture *texture = (GX2Texture *) malloc(sizeof(GX2Texture));
|
||||
*texture = {};
|
||||
|
||||
texture->surface.width = width;
|
||||
texture->surface.height = height;
|
||||
texture->surface.depth = 1;
|
||||
texture->surface.mipLevels = 1;
|
||||
texture->surface.format = GX2_SURFACE_FORMAT_UNORM_R8_G8_B8_A8;
|
||||
texture->surface.aa = GX2_AA_MODE1X;
|
||||
texture->surface.use = GX2_SURFACE_USE_TEXTURE;
|
||||
texture->surface.dim = GX2_SURFACE_DIM_TEXTURE_2D;
|
||||
texture->surface.tileMode = GX2_TILE_MODE_LINEAR_ALIGNED;
|
||||
texture->surface.swizzle = 0;
|
||||
texture->viewFirstMip = 0;
|
||||
texture->viewNumMips = 1;
|
||||
texture->viewFirstSlice = 0;
|
||||
texture->viewNumSlices = 1;
|
||||
texture->compMap = 0x0010203;
|
||||
GX2CalcSurfaceSizeAndAlignment(&texture->surface);
|
||||
GX2InitTextureRegs(texture);
|
||||
|
||||
if (texture->surface.imageSize == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
texture->surface.image = memalign(texture->surface.alignment, texture->surface.imageSize);
|
||||
if (!texture->surface.image) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (uint32_t y = 0; y < height; y++) {
|
||||
uint32_t *out_data = (uint32_t *) texture->surface.image + (y * texture->surface.pitch);
|
||||
for (uint32_t x = 0; x < width; x++) {
|
||||
int index = sizeof(TGA_HEADER) + (3 * width * (height - 1 - y)) + (3 * x);
|
||||
|
||||
int b = data[index + 0] & 0xFF;
|
||||
int g = data[index + 1] & 0xFF;
|
||||
int r = data[index + 2] & 0xFF;
|
||||
|
||||
*out_data = r << 24 | g << 16 | b << 8 | 0xFF;
|
||||
out_data++;
|
||||
}
|
||||
}
|
||||
|
||||
// todo: create texture with optimal tile format and use GX2CopySurface to convert from linear to tiled format
|
||||
GX2Invalidate(GX2_INVALIDATE_MODE_CPU | GX2_INVALIDATE_MODE_TEXTURE, texture->surface.image, texture->surface.imageSize);
|
||||
|
||||
return texture;
|
||||
}
|
24
source/utils/TGATexture.h
Normal file
24
source/utils/TGATexture.h
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <gx2/texture.h>
|
||||
|
||||
struct WUT_PACKED TGA_HEADER {
|
||||
uint8_t identsize; // size of ID field that follows 18 byte header (0 usually)
|
||||
uint8_t colourmaptype; // type of colour map 0=none, 1=has palette
|
||||
uint8_t imagetype; // type of image 0=none,1=indexed,2=rgb,3=grey,+8=rle packed
|
||||
|
||||
uint8_t colourmapstart[2]; // first colour map entry in palette
|
||||
uint8_t colourmaplength[2]; // number of colours in palette
|
||||
uint8_t colourmapbits; // number of bits per palette entry 15,16,24,32
|
||||
|
||||
uint16_t xstart; // image x origin
|
||||
uint16_t ystart; // image y origin
|
||||
uint16_t width; // image width in pixels
|
||||
uint16_t height; // image height in pixels
|
||||
uint8_t bits; // image bits per pixel 8,16,24,32
|
||||
uint8_t descriptor; // image descriptor bits (vh flip bits)
|
||||
};
|
||||
|
||||
// quick and dirty 24-bit TGA loader
|
||||
GX2Texture *TGA_LoadTexture(std::span<uint8_t> data);
|
549
source/utils/gfx.c
Normal file
549
source/utils/gfx.c
Normal file
@ -0,0 +1,549 @@
|
||||
/*
|
||||
* Based on https://github.com/devkitPro/wut/blob/4933211d7ba86d4dd45c8525fc83747d799ecf31/libraries/libwhb/src/gfx.c
|
||||
*/
|
||||
#include "logger.h"
|
||||
#include <avm/drc.h>
|
||||
#include <avm/tv.h>
|
||||
#include <coreinit/debug.h>
|
||||
#include <coreinit/memdefaultheap.h>
|
||||
#include <coreinit/memexpheap.h>
|
||||
#include <coreinit/memfrmheap.h>
|
||||
#include <coreinit/memheap.h>
|
||||
#include <coreinit/savedframe.h>
|
||||
#include <gx2/clear.h>
|
||||
#include <gx2/context.h>
|
||||
#include <gx2/display.h>
|
||||
#include <gx2/event.h>
|
||||
#include <gx2/mem.h>
|
||||
#include <gx2/registers.h>
|
||||
#include <gx2/shaders.h>
|
||||
#include <gx2/state.h>
|
||||
#include <gx2/surface.h>
|
||||
#include <gx2/swap.h>
|
||||
#include <gx2/utils.h>
|
||||
#include <gx2r/buffer.h>
|
||||
#include <gx2r/mem.h>
|
||||
#include <proc_ui/procui.h>
|
||||
#include <string.h>
|
||||
#include <whb/gfx.h>
|
||||
#include <whb/log.h>
|
||||
|
||||
#define WHB_GFX_COMMAND_BUFFER_POOL_SIZE (0x400000)
|
||||
|
||||
static void *sCommandBufferPool = NULL;
|
||||
static GX2DrcRenderMode sDrcRenderMode;
|
||||
static void *sDrcScanBuffer = NULL;
|
||||
static uint32_t sDrcScanBufferSize = 0;
|
||||
static GX2SurfaceFormat sDrcSurfaceFormat;
|
||||
static GX2TVRenderMode sTvRenderMode;
|
||||
static void *sTvScanBuffer = NULL;
|
||||
static uint32_t sTvScanBufferSize = 0;
|
||||
static GX2SurfaceFormat sTvSurfaceFormat;
|
||||
static GX2SurfaceFormat sDrcSurfaceFormat;
|
||||
static GX2ColorBuffer sTvColourBuffer = {0};
|
||||
static GX2ColorBuffer sDrcColourBuffer = {0};
|
||||
static GX2ContextState *sTvContextState = NULL;
|
||||
static GX2ContextState *sDrcContextState = NULL;
|
||||
static BOOL sGpuTimedOut = FALSE;
|
||||
|
||||
static void *sGfxHeapForeground = NULL;
|
||||
|
||||
static void *AllocMEM2(uint32_t size, uint32_t alignment) {
|
||||
void *block;
|
||||
|
||||
if (alignment < 4) {
|
||||
alignment = 4;
|
||||
}
|
||||
|
||||
block = MEMAllocFromDefaultHeapEx(size, alignment);
|
||||
if (!block) {
|
||||
OSFatal("AutobootModule: Failed to allocate memory from MEM2");
|
||||
}
|
||||
return block;
|
||||
}
|
||||
|
||||
static void FreeMEM2(void *block) {
|
||||
MEMFreeToDefaultHeap(block);
|
||||
}
|
||||
|
||||
static void *AllocBucket(uint32_t size, uint32_t alignment) {
|
||||
void *block;
|
||||
|
||||
if (!sGfxHeapForeground) {
|
||||
DEBUG_FUNCTION_LINE_ERR("sGfxHeapForeground was NULL");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (alignment < 4) {
|
||||
alignment = 4;
|
||||
}
|
||||
|
||||
block = MEMAllocFromExpHeapEx(sGfxHeapForeground, size, alignment);
|
||||
if (!block) {
|
||||
DEBUG_FUNCTION_LINE_ERR("Failed to allocate memory from bucket");
|
||||
}
|
||||
return block;
|
||||
}
|
||||
|
||||
static void FreeBucket(void *block) {
|
||||
if (!sGfxHeapForeground) {
|
||||
DEBUG_FUNCTION_LINE_ERR("sGfxHeapForeground was NULL");
|
||||
return;
|
||||
}
|
||||
|
||||
MEMFreeToExpHeap(sGfxHeapForeground, block);
|
||||
}
|
||||
|
||||
static void *
|
||||
GfxGX2RAlloc(GX2RResourceFlags flags, uint32_t size, uint32_t alignment) {
|
||||
return AllocMEM2(size, alignment);
|
||||
}
|
||||
|
||||
static void
|
||||
GfxGX2RFree(GX2RResourceFlags flags, void *block) {
|
||||
return FreeMEM2(block);
|
||||
}
|
||||
|
||||
static void
|
||||
GfxInitTvColourBuffer(GX2ColorBuffer *cb,
|
||||
uint32_t width,
|
||||
uint32_t height,
|
||||
GX2SurfaceFormat format,
|
||||
GX2AAMode aa) {
|
||||
memset(cb, 0, sizeof(GX2ColorBuffer));
|
||||
cb->surface.use = GX2_SURFACE_USE_TEXTURE_COLOR_BUFFER_TV;
|
||||
cb->surface.dim = GX2_SURFACE_DIM_TEXTURE_2D;
|
||||
cb->surface.width = width;
|
||||
cb->surface.height = height;
|
||||
cb->surface.depth = 1;
|
||||
cb->surface.mipLevels = 1;
|
||||
cb->surface.format = format;
|
||||
cb->surface.aa = aa;
|
||||
cb->surface.tileMode = GX2_TILE_MODE_DEFAULT;
|
||||
cb->viewNumSlices = 1;
|
||||
GX2CalcSurfaceSizeAndAlignment(&cb->surface);
|
||||
GX2InitColorBufferRegs(cb);
|
||||
}
|
||||
|
||||
static uint32_t InitMemory() {
|
||||
// Allocate TV scan buffer.
|
||||
sTvScanBuffer = AllocBucket(sTvScanBufferSize, GX2_SCAN_BUFFER_ALIGNMENT);
|
||||
if (!sTvScanBuffer) {
|
||||
DEBUG_FUNCTION_LINE_INFO("%s: sTvScanBuffer = AllocBucket(0x%X, 0x%X) failed",
|
||||
__FUNCTION__,
|
||||
sTvScanBufferSize,
|
||||
GX2_SCAN_BUFFER_ALIGNMENT);
|
||||
goto error;
|
||||
}
|
||||
GX2Invalidate(GX2_INVALIDATE_MODE_CPU, sTvScanBuffer, sTvScanBufferSize);
|
||||
GX2SetTVBuffer(sTvScanBuffer, sTvScanBufferSize, sTvRenderMode, sTvSurfaceFormat, GX2_BUFFERING_MODE_SINGLE);
|
||||
|
||||
// Allocate TV colour buffer.
|
||||
sTvColourBuffer.surface.image = AllocMEM2(sTvColourBuffer.surface.imageSize, sTvColourBuffer.surface.alignment);
|
||||
if (!sTvColourBuffer.surface.image) {
|
||||
DEBUG_FUNCTION_LINE_INFO("%s: sTvColourBuffer = AllocMEM2(0x%X, 0x%X) failed",
|
||||
__FUNCTION__,
|
||||
sTvColourBuffer.surface.imageSize,
|
||||
sTvColourBuffer.surface.alignment);
|
||||
goto error;
|
||||
}
|
||||
GX2Invalidate(GX2_INVALIDATE_MODE_CPU, sTvColourBuffer.surface.image, sTvColourBuffer.surface.imageSize);
|
||||
|
||||
// Allocate DRC scan buffer.
|
||||
sDrcScanBuffer = AllocBucket(sDrcScanBufferSize, GX2_SCAN_BUFFER_ALIGNMENT);
|
||||
if (!sDrcScanBuffer) {
|
||||
DEBUG_FUNCTION_LINE_INFO("%s: sDrcScanBuffer = AllocBucket(0x%X, 0x%X) failed",
|
||||
__FUNCTION__,
|
||||
sDrcScanBufferSize,
|
||||
GX2_SCAN_BUFFER_ALIGNMENT);
|
||||
goto error;
|
||||
}
|
||||
GX2Invalidate(GX2_INVALIDATE_MODE_CPU, sDrcScanBuffer, sDrcScanBufferSize);
|
||||
GX2SetDRCBuffer(sDrcScanBuffer, sDrcScanBufferSize, sDrcRenderMode, sDrcSurfaceFormat, GX2_BUFFERING_MODE_SINGLE);
|
||||
|
||||
// Allocate DRC colour buffer.
|
||||
sDrcColourBuffer.surface.image = AllocMEM2(sDrcColourBuffer.surface.imageSize, sDrcColourBuffer.surface.alignment);
|
||||
if (!sDrcColourBuffer.surface.image) {
|
||||
DEBUG_FUNCTION_LINE_INFO("%s: sDrcColourBuffer = AllocMEM2(0x%X, 0x%X) failed",
|
||||
__FUNCTION__,
|
||||
sDrcColourBuffer.surface.imageSize,
|
||||
sDrcColourBuffer.surface.alignment);
|
||||
goto error;
|
||||
}
|
||||
GX2Invalidate(GX2_INVALIDATE_MODE_CPU, sDrcColourBuffer.surface.image, sDrcColourBuffer.surface.imageSize);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static uint32_t DeinitMemory() {
|
||||
if (sTvScanBuffer) {
|
||||
FreeBucket(sTvScanBuffer);
|
||||
sTvScanBuffer = NULL;
|
||||
}
|
||||
|
||||
if (sTvColourBuffer.surface.image) {
|
||||
FreeMEM2(sTvColourBuffer.surface.image);
|
||||
sTvColourBuffer.surface.image = NULL;
|
||||
}
|
||||
|
||||
if (sDrcScanBuffer) {
|
||||
FreeBucket(sDrcScanBuffer);
|
||||
sDrcScanBuffer = NULL;
|
||||
}
|
||||
|
||||
if (sDrcColourBuffer.surface.image) {
|
||||
FreeMEM2(sDrcColourBuffer.surface.image);
|
||||
sDrcColourBuffer.surface.image = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static BOOL initBucketHeap() {
|
||||
MEMHeapHandle heap = MEMGetBaseHeapHandle(MEM_BASE_HEAP_FG);
|
||||
uint32_t size;
|
||||
void *base;
|
||||
|
||||
size = MEMGetAllocatableSizeForFrmHeapEx(heap, 4);
|
||||
if (!size) {
|
||||
DEBUG_FUNCTION_LINE_WARN("%s: MEMAllocFromFrmHeapEx(heap, 0x%X, 4)", __FUNCTION__, size);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
base = MEMAllocFromFrmHeapEx(heap, size, 4);
|
||||
if (!base) {
|
||||
DEBUG_FUNCTION_LINE_WARN("%s: MEMGetAllocatableSizeForFrmHeapEx == 0", __FUNCTION__);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
sGfxHeapForeground = MEMCreateExpHeapEx(base, size, 0);
|
||||
if (!sGfxHeapForeground) {
|
||||
DEBUG_FUNCTION_LINE_WARN("%s: MEMCreateExpHeapEx(0x%08X, 0x%X, 0)", __FUNCTION__, base, size);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL deInitBucketHeap() {
|
||||
MEMHeapHandle foreground = MEMGetBaseHeapHandle(MEM_BASE_HEAP_FG);
|
||||
|
||||
if (sGfxHeapForeground) {
|
||||
MEMDestroyExpHeap(sGfxHeapForeground);
|
||||
sGfxHeapForeground = NULL;
|
||||
}
|
||||
|
||||
MEMFreeToFrmHeap(foreground, MEM_FRM_HEAP_FREE_ALL);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL GfxInit() {
|
||||
initBucketHeap();
|
||||
|
||||
uint32_t drcWidth, drcHeight;
|
||||
uint32_t tvWidth, tvHeight;
|
||||
uint32_t unk;
|
||||
|
||||
sCommandBufferPool = AllocMEM2(WHB_GFX_COMMAND_BUFFER_POOL_SIZE,
|
||||
GX2_COMMAND_BUFFER_ALIGNMENT);
|
||||
if (!sCommandBufferPool) {
|
||||
DEBUG_FUNCTION_LINE_INFO("%s: failed to allocate command buffer pool", __FUNCTION__);
|
||||
goto error;
|
||||
}
|
||||
|
||||
uint32_t initAttribs[] = {
|
||||
GX2_INIT_CMD_BUF_BASE, (uintptr_t) sCommandBufferPool,
|
||||
GX2_INIT_CMD_BUF_POOL_SIZE, WHB_GFX_COMMAND_BUFFER_POOL_SIZE,
|
||||
GX2_INIT_ARGC, 0,
|
||||
GX2_INIT_ARGV, 0,
|
||||
GX2_INIT_END};
|
||||
GX2Init(initAttribs);
|
||||
|
||||
// Clear frame information in saved foreground to avoid screen corruption
|
||||
__OSClearSavedFrame(OS_SAVED_FRAME_A, OS_SAVED_FRAME_SCREEN_TV);
|
||||
__OSClearSavedFrame(OS_SAVED_FRAME_A, OS_SAVED_FRAME_SCREEN_DRC);
|
||||
__OSClearSavedFrame(OS_SAVED_FRAME_B, OS_SAVED_FRAME_SCREEN_TV);
|
||||
__OSClearSavedFrame(OS_SAVED_FRAME_B, OS_SAVED_FRAME_SCREEN_DRC);
|
||||
|
||||
// Disable output until we have rendered something
|
||||
GX2SetTVEnable(FALSE);
|
||||
GX2SetDRCEnable(FALSE);
|
||||
|
||||
sDrcRenderMode = GX2GetSystemDRCMode();
|
||||
sTvSurfaceFormat = GX2_SURFACE_FORMAT_UNORM_R8_G8_B8_A8;
|
||||
sDrcSurfaceFormat = GX2_SURFACE_FORMAT_UNORM_R8_G8_B8_A8;
|
||||
|
||||
switch (GX2GetSystemTVScanMode()) {
|
||||
case GX2_TV_SCAN_MODE_480I:
|
||||
case GX2_TV_SCAN_MODE_480P:
|
||||
sTvRenderMode = GX2_TV_RENDER_MODE_WIDE_480P;
|
||||
tvWidth = 854;
|
||||
tvHeight = 480;
|
||||
break;
|
||||
case GX2_TV_SCAN_MODE_1080I:
|
||||
case GX2_TV_SCAN_MODE_1080P:
|
||||
sTvRenderMode = GX2_TV_RENDER_MODE_WIDE_1080P;
|
||||
tvWidth = 1920;
|
||||
tvHeight = 1080;
|
||||
break;
|
||||
case GX2_TV_SCAN_MODE_720P:
|
||||
default:
|
||||
sTvRenderMode = GX2_TV_RENDER_MODE_WIDE_720P;
|
||||
tvWidth = 1280;
|
||||
tvHeight = 720;
|
||||
break;
|
||||
}
|
||||
|
||||
drcWidth = 854;
|
||||
drcHeight = 480;
|
||||
|
||||
// Setup TV and DRC buffers - they will be allocated in GfxProcCallbackAcquired.
|
||||
GX2CalcTVSize(sTvRenderMode, sTvSurfaceFormat, GX2_BUFFERING_MODE_SINGLE, &sTvScanBufferSize, &unk);
|
||||
GfxInitTvColourBuffer(&sTvColourBuffer, tvWidth, tvHeight, sTvSurfaceFormat, GX2_AA_MODE1X);
|
||||
|
||||
GX2CalcDRCSize(sDrcRenderMode, sDrcSurfaceFormat, GX2_BUFFERING_MODE_DOUBLE, &sDrcScanBufferSize, &unk);
|
||||
GfxInitTvColourBuffer(&sDrcColourBuffer, drcWidth, drcHeight, sDrcSurfaceFormat, GX2_AA_MODE1X);
|
||||
|
||||
GX2CalcDRCSize(sDrcRenderMode, sDrcSurfaceFormat, GX2_BUFFERING_MODE_SINGLE, &sDrcScanBufferSize, &unk);
|
||||
if (InitMemory() != 0) {
|
||||
DEBUG_FUNCTION_LINE_INFO("%s: GfxProcCallbackAcquired failed", __FUNCTION__);
|
||||
goto error;
|
||||
}
|
||||
|
||||
GX2RSetAllocator(&GfxGX2RAlloc, &GfxGX2RFree);
|
||||
|
||||
// Initialise TV context state.
|
||||
sTvContextState = AllocMEM2(sizeof(GX2ContextState), GX2_CONTEXT_STATE_ALIGNMENT);
|
||||
if (!sTvContextState) {
|
||||
DEBUG_FUNCTION_LINE_INFO("%s: failed to allocate sTvContextState", __FUNCTION__);
|
||||
goto error;
|
||||
}
|
||||
GX2SetupContextStateEx(sTvContextState, TRUE);
|
||||
GX2SetContextState(sTvContextState);
|
||||
GX2SetColorBuffer(&sTvColourBuffer, GX2_RENDER_TARGET_0);
|
||||
GX2SetViewport(0, 0, (float) sTvColourBuffer.surface.width, (float) sTvColourBuffer.surface.height, 0.0f, 1.0f);
|
||||
GX2SetScissor(0, 0, (float) sTvColourBuffer.surface.width, (float) sTvColourBuffer.surface.height);
|
||||
GX2SetTVScale((float) sTvColourBuffer.surface.width, (float) sTvColourBuffer.surface.height);
|
||||
|
||||
// Initialise DRC context state.
|
||||
sDrcContextState = AllocMEM2(sizeof(GX2ContextState), GX2_CONTEXT_STATE_ALIGNMENT);
|
||||
if (!sDrcContextState) {
|
||||
WHBLogPrintf("%s: failed to allocate sDrcContextState", __FUNCTION__);
|
||||
goto error;
|
||||
}
|
||||
GX2SetupContextStateEx(sDrcContextState, TRUE);
|
||||
GX2SetContextState(sDrcContextState);
|
||||
GX2SetColorBuffer(&sDrcColourBuffer, GX2_RENDER_TARGET_0);
|
||||
GX2SetViewport(0, 0, (float) sDrcColourBuffer.surface.width, (float) sDrcColourBuffer.surface.height, 0.0f, 1.0f);
|
||||
GX2SetScissor(0, 0, (float) sDrcColourBuffer.surface.width, (float) sDrcColourBuffer.surface.height);
|
||||
GX2SetDRCScale((float) sDrcColourBuffer.surface.width, (float) sDrcColourBuffer.surface.height);
|
||||
|
||||
// Set 60fps VSync
|
||||
GX2SetSwapInterval(1);
|
||||
|
||||
return TRUE;
|
||||
|
||||
error:
|
||||
if (sCommandBufferPool) {
|
||||
FreeMEM2(sCommandBufferPool);
|
||||
sCommandBufferPool = NULL;
|
||||
}
|
||||
|
||||
if (sTvScanBuffer) {
|
||||
FreeBucket(sTvScanBuffer);
|
||||
sTvScanBuffer = NULL;
|
||||
}
|
||||
|
||||
if (sTvColourBuffer.surface.image) {
|
||||
FreeMEM2(sTvColourBuffer.surface.image);
|
||||
sTvColourBuffer.surface.image = NULL;
|
||||
}
|
||||
|
||||
if (sTvContextState) {
|
||||
FreeMEM2(sTvContextState);
|
||||
sTvContextState = NULL;
|
||||
}
|
||||
|
||||
if (sDrcContextState) {
|
||||
FreeMEM2(sDrcContextState);
|
||||
sDrcContextState = NULL;
|
||||
}
|
||||
|
||||
if (sDrcScanBuffer) {
|
||||
FreeBucket(sDrcScanBuffer);
|
||||
sDrcScanBuffer = NULL;
|
||||
}
|
||||
|
||||
if (sDrcColourBuffer.surface.image) {
|
||||
FreeMEM2(sDrcColourBuffer.surface.image);
|
||||
sDrcColourBuffer.surface.image = NULL;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void GfxShutdown() {
|
||||
if (sGpuTimedOut) {
|
||||
GX2ResetGPU(0);
|
||||
sGpuTimedOut = FALSE;
|
||||
}
|
||||
|
||||
GX2RSetAllocator(NULL, NULL);
|
||||
|
||||
GX2Shutdown();
|
||||
|
||||
DeinitMemory();
|
||||
|
||||
if (sTvContextState) {
|
||||
FreeMEM2(sTvContextState);
|
||||
sTvContextState = NULL;
|
||||
}
|
||||
|
||||
if (sDrcContextState) {
|
||||
FreeMEM2(sDrcContextState);
|
||||
sDrcContextState = NULL;
|
||||
}
|
||||
|
||||
if (sCommandBufferPool) {
|
||||
FreeMEM2(sCommandBufferPool);
|
||||
sCommandBufferPool = NULL;
|
||||
}
|
||||
|
||||
deInitBucketHeap();
|
||||
}
|
||||
|
||||
void GfxBeginRender() {
|
||||
uint32_t swapCount, flipCount;
|
||||
OSTime lastFlip, lastVsync;
|
||||
uint32_t waitCount = 0;
|
||||
|
||||
while (1) {
|
||||
GX2GetSwapStatus(&swapCount, &flipCount, &lastFlip, &lastVsync);
|
||||
|
||||
if (flipCount >= swapCount) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (waitCount >= 10) {
|
||||
WHBLogPrint("WHBGfxBeginRender wait for swap timed out");
|
||||
sGpuTimedOut = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
waitCount++;
|
||||
GX2WaitForVsync();
|
||||
}
|
||||
}
|
||||
|
||||
void GfxBeginRenderDRC() {
|
||||
GX2SetContextState(sDrcContextState);
|
||||
}
|
||||
|
||||
void GfxFinishRenderDRC() {
|
||||
GX2CopyColorBufferToScanBuffer(&sDrcColourBuffer, GX2_SCAN_TARGET_DRC);
|
||||
}
|
||||
|
||||
|
||||
void GfxBeginRenderTV() {
|
||||
GX2SetContextState(sTvContextState);
|
||||
}
|
||||
|
||||
void GfxFinishRenderTV() {
|
||||
GX2CopyColorBufferToScanBuffer(&sTvColourBuffer, GX2_SCAN_TARGET_TV);
|
||||
}
|
||||
|
||||
void GfxFinishRender() {
|
||||
GX2SwapScanBuffers();
|
||||
GX2Flush();
|
||||
GX2DrawDone();
|
||||
GX2SetTVEnable(TRUE);
|
||||
GX2SetDRCEnable(TRUE);
|
||||
}
|
||||
|
||||
BOOL GfxInitFetchShader(WHBGfxShaderGroup *group) {
|
||||
uint32_t size = GX2CalcFetchShaderSizeEx(group->numAttributes,
|
||||
GX2_FETCH_SHADER_TESSELLATION_NONE,
|
||||
GX2_TESSELLATION_MODE_DISCRETE);
|
||||
group->fetchShaderProgram = AllocMEM2(size, GX2_SHADER_PROGRAM_ALIGNMENT);
|
||||
|
||||
GX2InitFetchShaderEx(&group->fetchShader,
|
||||
group->fetchShaderProgram,
|
||||
group->numAttributes,
|
||||
group->attributes,
|
||||
GX2_FETCH_SHADER_TESSELLATION_NONE,
|
||||
GX2_TESSELLATION_MODE_DISCRETE);
|
||||
|
||||
GX2Invalidate(GX2_INVALIDATE_MODE_CPU_SHADER, group->fetchShaderProgram, size);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static uint32_t
|
||||
GfxGetAttribFormatSel(GX2AttribFormat format) {
|
||||
switch (format) {
|
||||
case GX2_ATTRIB_FORMAT_UNORM_8:
|
||||
case GX2_ATTRIB_FORMAT_UINT_8:
|
||||
case GX2_ATTRIB_FORMAT_SNORM_8:
|
||||
case GX2_ATTRIB_FORMAT_SINT_8:
|
||||
case GX2_ATTRIB_FORMAT_FLOAT_32:
|
||||
return GX2_SEL_MASK(GX2_SQ_SEL_X, GX2_SQ_SEL_0, GX2_SQ_SEL_0, GX2_SQ_SEL_1);
|
||||
case GX2_ATTRIB_FORMAT_UNORM_8_8:
|
||||
case GX2_ATTRIB_FORMAT_UINT_8_8:
|
||||
case GX2_ATTRIB_FORMAT_SNORM_8_8:
|
||||
case GX2_ATTRIB_FORMAT_SINT_8_8:
|
||||
case GX2_ATTRIB_FORMAT_FLOAT_32_32:
|
||||
return GX2_SEL_MASK(GX2_SQ_SEL_X, GX2_SQ_SEL_Y, GX2_SQ_SEL_0, GX2_SQ_SEL_1);
|
||||
case GX2_ATTRIB_FORMAT_FLOAT_32_32_32:
|
||||
return GX2_SEL_MASK(GX2_SQ_SEL_X, GX2_SQ_SEL_Y, GX2_SQ_SEL_Z, GX2_SQ_SEL_1);
|
||||
case GX2_ATTRIB_FORMAT_UNORM_8_8_8_8:
|
||||
case GX2_ATTRIB_FORMAT_UINT_8_8_8_8:
|
||||
case GX2_ATTRIB_FORMAT_SNORM_8_8_8_8:
|
||||
case GX2_ATTRIB_FORMAT_SINT_8_8_8_8:
|
||||
case GX2_ATTRIB_FORMAT_FLOAT_32_32_32_32:
|
||||
return GX2_SEL_MASK(GX2_SQ_SEL_X, GX2_SQ_SEL_Y, GX2_SQ_SEL_Z, GX2_SQ_SEL_W);
|
||||
break;
|
||||
default:
|
||||
return GX2_SEL_MASK(GX2_SQ_SEL_0, GX2_SQ_SEL_0, GX2_SQ_SEL_0, GX2_SQ_SEL_1);
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t
|
||||
GfxGetVertexAttribVarLocation(const GX2VertexShader *shader,
|
||||
const char *name) {
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < shader->attribVarCount; ++i) {
|
||||
if (strcmp(shader->attribVars[i].name, name) == 0) {
|
||||
return shader->attribVars[i].location;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
BOOL GfxInitShaderAttribute(WHBGfxShaderGroup *group,
|
||||
const char *name,
|
||||
uint32_t buffer,
|
||||
uint32_t offset,
|
||||
GX2AttribFormat format) {
|
||||
GX2AttribStream *attrib;
|
||||
int32_t location;
|
||||
|
||||
location = GfxGetVertexAttribVarLocation(group->vertexShader, name);
|
||||
if (location == -1) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
attrib = &group->attributes[group->numAttributes++];
|
||||
attrib->location = location;
|
||||
attrib->buffer = buffer;
|
||||
attrib->offset = offset;
|
||||
attrib->format = format;
|
||||
attrib->type = GX2_ATTRIB_INDEX_PER_VERTEX;
|
||||
attrib->aluDivisor = 0;
|
||||
attrib->mask = GfxGetAttribFormatSel(format);
|
||||
attrib->endianSwap = GX2_ENDIAN_SWAP_DEFAULT;
|
||||
return TRUE;
|
||||
}
|
40
source/utils/gfx.h
Normal file
40
source/utils/gfx.h
Normal file
@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
#include <gx2/context.h>
|
||||
#include <gx2/shaders.h>
|
||||
#include <gx2/texture.h>
|
||||
#include <whb/gfx.h>
|
||||
#include <wut.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
BOOL GfxInit();
|
||||
|
||||
void GfxShutdown();
|
||||
|
||||
void GfxBeginRender();
|
||||
|
||||
void GfxFinishRender();
|
||||
|
||||
void GfxBeginRenderDRC();
|
||||
|
||||
void GfxFinishRenderDRC();
|
||||
|
||||
void GfxBeginRenderTV();
|
||||
|
||||
void GfxFinishRenderTV();
|
||||
|
||||
BOOL GfxInitShaderAttribute(WHBGfxShaderGroup *group,
|
||||
const char *name,
|
||||
uint32_t buffer,
|
||||
uint32_t offset,
|
||||
GX2AttribFormat format);
|
||||
|
||||
BOOL GfxInitFetchShader(WHBGfxShaderGroup *group);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @} */
|
2
source/version.h
Normal file
2
source/version.h
Normal file
@ -0,0 +1,2 @@
|
||||
#pragma once
|
||||
#define AUTOBOOT_MODULE_VERSION_EXTRA ""
|
Loading…
Reference in New Issue
Block a user