WiiUIdent v1.0
18
.github/workflows/build.yml
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
name: Build
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build-binary:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Build binary
|
||||
run: |
|
||||
docker run --rm -v ${PWD}:/project garyodernichts/wiiuident_builder make
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: WiiUIdent
|
||||
path: |
|
||||
*.wuhb
|
||||
*.rpx
|
32
Dockerfile
Normal file
@ -0,0 +1,32 @@
|
||||
FROM ghcr.io/wiiu-env/devkitppc:20230326
|
||||
|
||||
COPY --from=ghcr.io/wiiu-env/libmocha:20220903 /artifacts $DEVKITPRO
|
||||
|
||||
# build and install latest wut
|
||||
WORKDIR /
|
||||
RUN \
|
||||
mkdir wut && \
|
||||
cd wut && \
|
||||
git init . && \
|
||||
git remote add origin https://github.com/devkitPro/wut.git && \
|
||||
git fetch --depth 1 origin 451a1828f7646053b59ebacd813135e0300c67e8 && \
|
||||
git checkout FETCH_HEAD
|
||||
WORKDIR /wut
|
||||
RUN make -j$(nproc)
|
||||
RUN make install
|
||||
|
||||
# build and install latest sdl
|
||||
WORKDIR /
|
||||
RUN \
|
||||
mkdir SDL && \
|
||||
cd SDL && \
|
||||
git init . && \
|
||||
git remote add origin https://github.com/GaryOderNichts/SDL.git && \
|
||||
git fetch --depth 1 origin 687746c8c9514b5d48d5f9665a1d5fa36c5e5547 && \
|
||||
git checkout FETCH_HEAD
|
||||
WORKDIR /SDL
|
||||
RUN /opt/devkitpro/portlibs/wiiu/bin/powerpc-eabi-cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$DEVKITPRO/portlibs/wiiu
|
||||
RUN cmake --build build
|
||||
RUN cmake --install build
|
||||
|
||||
WORKDIR /project
|
339
LICENSE
Normal file
@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
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
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<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 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
41
Makefile
@ -13,9 +13,11 @@ TOPDIR ?= $(CURDIR)
|
||||
# APP_SHORTNAME sets the short name of the application
|
||||
# APP_AUTHOR sets the author of the application
|
||||
#-------------------------------------------------------------------------------
|
||||
APP_NAME := mdinfo
|
||||
APP_SHORTNAME := mdinfo
|
||||
APP_NAME := WiiUIdent
|
||||
APP_SHORTNAME := WiiUIdent
|
||||
APP_AUTHOR := GaryOderNichts
|
||||
APP_VERSION := 1.0
|
||||
DATABASE_URL := wiiu.gerbilsoft.com
|
||||
|
||||
include $(DEVKITPRO)/wut/share/wut_rules
|
||||
|
||||
@ -30,13 +32,13 @@ include $(DEVKITPRO)/wut/share/wut_rules
|
||||
# TV_SPLASH is the image displayed during bootup on the TV, leave blank to use default rule
|
||||
# DRC_SPLASH is the image displayed during bootup on the DRC, leave blank to use default rule
|
||||
#-------------------------------------------------------------------------------
|
||||
TARGET := $(notdir $(CURDIR))
|
||||
TARGET := wiiuident
|
||||
BUILD := build
|
||||
SOURCES := .
|
||||
SOURCES := source source/screens source/system
|
||||
DATA := data
|
||||
INCLUDES := include
|
||||
INCLUDES := source include
|
||||
CONTENT :=
|
||||
ICON := icon.png
|
||||
ICON := res/icon.png
|
||||
TV_SPLASH :=
|
||||
DRC_SPLASH :=
|
||||
|
||||
@ -46,14 +48,15 @@ DRC_SPLASH :=
|
||||
CFLAGS := -Wall -O2 -ffunction-sections \
|
||||
$(MACHDEP)
|
||||
|
||||
CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__
|
||||
CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ -DAPP_VERSION=\"$(APP_VERSION)\" \
|
||||
-DDATABASE_URL=\"$(DATABASE_URL)\"
|
||||
|
||||
CXXFLAGS := $(CFLAGS)
|
||||
CXXFLAGS := $(CFLAGS) -std=gnu++20
|
||||
|
||||
ASFLAGS := $(ARCH)
|
||||
LDFLAGS = $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map)
|
||||
|
||||
LIBS := -lmocha -lwut
|
||||
LIBS := -lcurl -lmbedtls -lmbedcrypto -lmbedx509 -lSDL2 -lSDL2_ttf -lfreetype -lpng -lbz2 -lz -lmocha -lwut
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level
|
||||
@ -103,7 +106,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$(CURDIR)/$(BUILD) -I$(DEVKITPRO)/portlibs/wiiu/include/SDL2
|
||||
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
|
||||
@ -174,6 +177,24 @@ $(OFILES_SRC) : $(HFILES_BIN)
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
%.ttf.o %_ttf.h : %.ttf
|
||||
#-------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
%.bdf.o %_bdf.h : %.bdf
|
||||
#-------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
%.pem.o %_pem.h : %.pem
|
||||
#-------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
-include $(DEPENDS)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
82
README.md
@ -1,14 +1,74 @@
|
||||
![](icon.png)
|
||||
# mdinfo
|
||||
![](res/banner.png)
|
||||
A Wii U Identification homebrew, which display information about console hardware components, software/hardware versions and much more.
|
||||
|
||||
Quick and simple tool to display info about IOSU's memory devices.
|
||||
Can be used to figure out what eMMC chip is built into a console.
|
||||
> :information_source: **WiiUIdent requires the [MochaPayload](https://github.com/wiiu-env/MochaPayload)!**
|
||||
> Make sure to update to [Aroma](https://aroma.foryour.cafe) or [Tiramisu](https://tiramisu.foryour.cafe), in order to use this application.
|
||||
|
||||
**Note: You need the [MochaPayload](https://github.com/wiiu-env/MochaPayload) for this to work**
|
||||
## Features
|
||||
WiiUIdent currently displays:
|
||||
|
||||
## Currently displays:
|
||||
* Manufacturer ID
|
||||
-> Shows known name and type for that ID
|
||||
* Product Revision
|
||||
* Product Name
|
||||
* CID and CSD
|
||||
- Identification
|
||||
- Model
|
||||
- Serial
|
||||
- Production Data
|
||||
- Keyset
|
||||
- Hardware
|
||||
- Type
|
||||
- Version
|
||||
- Board Type
|
||||
- SATA Device
|
||||
- DDR3 Size/Speed/Vendor
|
||||
- Region
|
||||
- Product Area
|
||||
- Game Region
|
||||
- Versions
|
||||
- System Version
|
||||
- Boot1 Version
|
||||
- MLC/SD Card information
|
||||
- Type
|
||||
- Manufacturer
|
||||
- Product Name
|
||||
- Product Revision
|
||||
- Production Date (MLC only)
|
||||
- Size
|
||||
- CID/CSD
|
||||
|
||||
## System Database
|
||||
WiiUIdent comes with an option to optionally upload system information to a database. This allows collecting various statistics about Wii U consoles.
|
||||
After submitting your data, your system information will be added to the database with a System ID, which will be displayed on the console.
|
||||
The database is publicly accessible but personally identifying information will be kept confidential.
|
||||
[The database can be found here!](https://wiiu.gerbilsoft.com/)
|
||||
|
||||
## Screenshots
|
||||
![](res/screenshot0.png)
|
||||
![](res/screenshot1.png)
|
||||
![](res/screenshot2.png)
|
||||
![](res/screenshot3.png)
|
||||
![](res/screenshot4.png)
|
||||
![](res/screenshot5.png)
|
||||
|
||||
## Building
|
||||
For building you need:
|
||||
- [wut](https://github.com/devkitPro/wut)
|
||||
- [libmocha](https://github.com/wiiu-env/libmocha)
|
||||
- [SDL2 v2.22](https://github.com/GaryOderNichts/SDL/tree/wiiu-sdl2-2.0.22)
|
||||
- wiiu-curl
|
||||
- wiiu-mbedtls
|
||||
|
||||
You can also build WiiUIdent using docker:
|
||||
```bash
|
||||
# Build docker image (only needed once)
|
||||
docker build . -t wiiuident_builder
|
||||
|
||||
# make
|
||||
docker run -it --rm -v ${PWD}:/project wiiuident_builder make
|
||||
|
||||
# make clean
|
||||
docker run -it --rm -v ${PWD}:/project wiiuident_builder make clean
|
||||
```
|
||||
A pre-built container is available as `garyodernichts/wiiuident_builder`.
|
||||
|
||||
## Additional Credits
|
||||
- [@GerbilSoft](https://github.com/GerbilSoft) for providing the System Database and the "System Information" implementation in the `recovery_menu`.
|
||||
- [FontAwesome](https://fontawesome.com/) for the icons.
|
||||
- [Terminus Font](https://terminus-font.sourceforge.net/) for the monospace font.
|
||||
|
3372
data/cacert.pem
Normal file
BIN
data/fa-solid-900.ttf
Normal file
52912
data/ter-u32b.bdf
Normal file
209
idsdb.h
@ -1,209 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// from https://kernel.googlesource.com/pub/scm/utils/mmc/mmc-utils/+/refs/heads/master/lsmmc.c
|
||||
|
||||
struct ids_database {
|
||||
char *type;
|
||||
int id;
|
||||
char *manufacturer;
|
||||
};
|
||||
|
||||
static struct ids_database unk_db = {
|
||||
.type = "unk",
|
||||
.manufacturer = "Unknown",
|
||||
};
|
||||
|
||||
static struct ids_database database[] = {
|
||||
{
|
||||
.type = "sd",
|
||||
.id = 0x01,
|
||||
.manufacturer = "Panasonic",
|
||||
},
|
||||
{
|
||||
.type = "sd",
|
||||
.id = 0x02,
|
||||
.manufacturer = "Toshiba/Kingston/Viking",
|
||||
},
|
||||
{
|
||||
.type = "sd",
|
||||
.id = 0x03,
|
||||
.manufacturer = "SanDisk",
|
||||
},
|
||||
{
|
||||
.type = "sd",
|
||||
.id = 0x08,
|
||||
.manufacturer = "Silicon Power",
|
||||
},
|
||||
{
|
||||
.type = "sd",
|
||||
.id = 0x18,
|
||||
.manufacturer = "Infineon",
|
||||
},
|
||||
{
|
||||
.type = "sd",
|
||||
.id = 0x1b,
|
||||
.manufacturer = "Transcend/Samsung",
|
||||
},
|
||||
{
|
||||
.type = "sd",
|
||||
.id = 0x1c,
|
||||
.manufacturer = "Transcend",
|
||||
},
|
||||
{
|
||||
.type = "sd",
|
||||
.id = 0x1d,
|
||||
.manufacturer = "Corsair/AData",
|
||||
},
|
||||
{
|
||||
.type = "sd",
|
||||
.id = 0x1e,
|
||||
.manufacturer = "Transcend",
|
||||
},
|
||||
{
|
||||
.type = "sd",
|
||||
.id = 0x1f,
|
||||
.manufacturer = "Kingston",
|
||||
},
|
||||
{
|
||||
.type = "sd",
|
||||
.id = 0x27,
|
||||
.manufacturer = "Delkin/Phison",
|
||||
},
|
||||
{
|
||||
.type = "sd",
|
||||
.id = 0x28,
|
||||
.manufacturer = "Lexar",
|
||||
},
|
||||
{
|
||||
.type = "sd",
|
||||
.id = 0x30,
|
||||
.manufacturer = "SanDisk",
|
||||
},
|
||||
{
|
||||
.type = "sd",
|
||||
.id = 0x31,
|
||||
.manufacturer = "Silicon Power",
|
||||
},
|
||||
{
|
||||
.type = "sd",
|
||||
.id = 0x33,
|
||||
.manufacturer = "STMicroelectronics",
|
||||
},
|
||||
{
|
||||
.type = "sd",
|
||||
.id = 0x41,
|
||||
.manufacturer = "Kingston",
|
||||
},
|
||||
{
|
||||
.type = "sd",
|
||||
.id = 0x6f,
|
||||
.manufacturer = "STMicroelectronics",
|
||||
},
|
||||
{
|
||||
.type = "sd",
|
||||
.id = 0x74,
|
||||
.manufacturer = "Transcend",
|
||||
},
|
||||
{
|
||||
.type = "sd",
|
||||
.id = 0x76,
|
||||
.manufacturer = "Patriot",
|
||||
},
|
||||
{
|
||||
.type = "sd",
|
||||
.id = 0x82,
|
||||
.manufacturer = "Gobe/Sony",
|
||||
},
|
||||
{
|
||||
.type = "sd",
|
||||
.id = 0x89,
|
||||
.manufacturer = "Unknown",
|
||||
},
|
||||
{
|
||||
.type = "sd",
|
||||
.id = 0x9e,
|
||||
.manufacturer = "PNY",
|
||||
},
|
||||
{
|
||||
.type = "mmc",
|
||||
.id = 0x00,
|
||||
.manufacturer = "SanDisk",
|
||||
},
|
||||
{
|
||||
.type = "mmc",
|
||||
.id = 0x02,
|
||||
.manufacturer = "Kingston/SanDisk",
|
||||
},
|
||||
{
|
||||
.type = "mmc",
|
||||
.id = 0x03,
|
||||
.manufacturer = "Toshiba",
|
||||
},
|
||||
{
|
||||
.type = "mmc",
|
||||
.id = 0x05,
|
||||
.manufacturer = "Unknown",
|
||||
},
|
||||
{
|
||||
.type = "mmc",
|
||||
.id = 0x06,
|
||||
.manufacturer = "Unknown",
|
||||
},
|
||||
{
|
||||
.type = "mmc",
|
||||
.id = 0x11,
|
||||
.manufacturer = "Toshiba",
|
||||
},
|
||||
{
|
||||
.type = "mmc",
|
||||
.id = 0x13,
|
||||
.manufacturer = "Micron",
|
||||
},
|
||||
{
|
||||
.type = "mmc",
|
||||
.id = 0x15,
|
||||
.manufacturer = "Samsung/SanDisk/LG",
|
||||
},
|
||||
{
|
||||
.type = "mmc",
|
||||
.id = 0x37,
|
||||
.manufacturer = "KingMax",
|
||||
},
|
||||
{
|
||||
.type = "mmc",
|
||||
.id = 0x44,
|
||||
.manufacturer = "SanDisk",
|
||||
},
|
||||
{
|
||||
.type = "mmc",
|
||||
.id = 0x2c,
|
||||
.manufacturer = "Kingston",
|
||||
},
|
||||
{
|
||||
.type = "mmc",
|
||||
.id = 0x70,
|
||||
.manufacturer = "Kingston",
|
||||
},
|
||||
{
|
||||
.type = "mmc",
|
||||
.id = 0xfe,
|
||||
.manufacturer = "Micron",
|
||||
},
|
||||
{
|
||||
.type = "mmc",
|
||||
.id = 0x90,
|
||||
.manufacturer = "Hynix",
|
||||
},
|
||||
};
|
||||
|
||||
static inline struct ids_database *find_by_id(int id)
|
||||
{
|
||||
unsigned int ids_cnt = sizeof(database) / sizeof(struct ids_database);
|
||||
for (int i = 0; i < ids_cnt; ++i) {
|
||||
if (database[i].id == id) {
|
||||
return &database[i];
|
||||
}
|
||||
}
|
||||
|
||||
return &unk_db;
|
||||
}
|
136
main.c
@ -1,136 +0,0 @@
|
||||
#include <whb/proc.h>
|
||||
#include <whb/log.h>
|
||||
#include <whb/log_udp.h>
|
||||
#include <whb/log_console.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <mocha/mocha.h>
|
||||
|
||||
#include "idsdb.h"
|
||||
|
||||
struct SALDeviceParams {
|
||||
uint32_t usrptr;
|
||||
uint32_t mid_prv;
|
||||
uint32_t device_type;
|
||||
uint32_t unk[16];
|
||||
char name0[128];
|
||||
char name1[128];
|
||||
char name2[128];
|
||||
uint32_t functions[12];
|
||||
};
|
||||
|
||||
struct MDBlkDriver {
|
||||
int32_t registered;
|
||||
int32_t unk[2];
|
||||
struct SALDeviceParams params;
|
||||
int sal_handle;
|
||||
int deviceId;
|
||||
uint8_t unk2[196];
|
||||
};
|
||||
static_assert(sizeof(struct MDBlkDriver) == 724, "MDBlkDriver: wrong size");
|
||||
|
||||
#define MDBLK_DRIVER_ADDRESS 0x11c39e78
|
||||
#define MD_DEVICE_POINTERS_ADDRESS 0x10899308
|
||||
|
||||
struct MDBlkDriver blkDrivers[2] = { 0 };
|
||||
|
||||
MochaUtilsStatus iosuKernRead32(uint32_t address, uint32_t* outBuffer, int count)
|
||||
{
|
||||
MochaUtilsStatus status = MOCHA_RESULT_SUCCESS;
|
||||
|
||||
for (int i = 0; i < count; ++i) {
|
||||
status = Mocha_IOSUKernelRead32(address + (i * 4), outBuffer + i);
|
||||
if (status != MOCHA_RESULT_SUCCESS) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static uint32_t getDeviceAddressByID(int id)
|
||||
{
|
||||
if (id - 0x42 >= 8) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t devicePointers[8];
|
||||
if (iosuKernRead32(MD_DEVICE_POINTERS_ADDRESS, devicePointers, 8) != MOCHA_RESULT_SUCCESS) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// virtual matches physical address for IOS-FS, no need to conversion
|
||||
return devicePointers[id - 0x42];
|
||||
}
|
||||
|
||||
int main(int argc, char const *argv[])
|
||||
{
|
||||
WHBProcInit();
|
||||
WHBLogUdpInit();
|
||||
WHBLogConsoleInit();
|
||||
|
||||
if (Mocha_InitLibrary() == MOCHA_RESULT_SUCCESS) {
|
||||
if (iosuKernRead32(MDBLK_DRIVER_ADDRESS, (uint32_t *) blkDrivers, sizeof(blkDrivers) / 4) == MOCHA_RESULT_SUCCESS) {
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
struct MDBlkDriver *drv = &blkDrivers[i];
|
||||
WHBLogPrintf("** Instance %d: (%s) Type: %d **", i + 1, drv->registered ? "Attached" : "Detached", drv->params.device_type);
|
||||
if (drv->registered) {
|
||||
uint16_t mid = drv->params.mid_prv >> 16;
|
||||
uint16_t prv = drv->params.mid_prv & 0xff;
|
||||
struct ids_database *db = find_by_id(mid);
|
||||
|
||||
WHBLogPrintf("Manufacturer ID: 0x%02x, Product revision: 0x%02x", mid, prv);
|
||||
WHBLogPrintf(" -> Manufacturer: '%s' Type: '%s'", db->manufacturer, db->type);
|
||||
WHBLogPrintf("Name 0: '%s' Name 1: '%s' Name 2: '%s'", drv->params.name0, drv->params.name1, drv->params.name2);
|
||||
|
||||
uint32_t deviceAddress = getDeviceAddressByID(drv->deviceId);
|
||||
if (!deviceAddress) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t cid[4];
|
||||
if (iosuKernRead32(deviceAddress + 0x58, cid, 4) != MOCHA_RESULT_SUCCESS) {
|
||||
continue;
|
||||
}
|
||||
|
||||
WHBLogPrintf("CID: %08x%08x%08x%08x", cid[0], cid[1], cid[2], cid[3]);
|
||||
|
||||
if(!strcmp(db->type,"mmc")){
|
||||
uint8_t month = (uint8_t)(cid[3] >> 12) & 0xf;
|
||||
uint16_t year = (uint8_t)(cid[3] >> 8) & 0xf;
|
||||
year += 1997;
|
||||
if(year < 2005)
|
||||
year += 0x10;
|
||||
WHBLogPrintf("Mfg Date: %02u/%u", month, year);
|
||||
}
|
||||
|
||||
|
||||
uint32_t csd[4];
|
||||
if (iosuKernRead32(deviceAddress + 0x68, csd, 4) != MOCHA_RESULT_SUCCESS) {
|
||||
continue;
|
||||
}
|
||||
|
||||
WHBLogPrintf("CSD: %08x%08x%08x%08x", csd[0], csd[1], csd[2], csd[3]);
|
||||
}
|
||||
|
||||
WHBLogPrintf("=================================================");
|
||||
}
|
||||
} else {
|
||||
WHBLogPrintf("Failed to read driver data");
|
||||
}
|
||||
|
||||
Mocha_DeInitLibrary();
|
||||
} else {
|
||||
WHBLogPrintf("Failed to initialize Mocha");
|
||||
}
|
||||
|
||||
while (WHBProcIsRunning()) {
|
||||
WHBLogConsoleDraw();
|
||||
}
|
||||
|
||||
WHBLogConsoleFree();
|
||||
WHBLogUdpDeinit();
|
||||
WHBProcShutdown();
|
||||
return 0;
|
||||
}
|
BIN
res/banner.png
Normal file
After Width: | Height: | Size: 979 KiB |
BIN
res/icon.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
res/screenshot0.png
Normal file
After Width: | Height: | Size: 277 KiB |
BIN
res/screenshot1.png
Normal file
After Width: | Height: | Size: 711 KiB |
BIN
res/screenshot2.png
Normal file
After Width: | Height: | Size: 615 KiB |
BIN
res/screenshot3.png
Normal file
After Width: | Height: | Size: 916 KiB |
BIN
res/screenshot4.png
Normal file
After Width: | Height: | Size: 292 KiB |
BIN
res/screenshot5.png
Normal file
After Width: | Height: | Size: 360 KiB |
290
source/Gfx.cpp
Normal file
@ -0,0 +1,290 @@
|
||||
#include "Gfx.hpp"
|
||||
#include "SDL_FontCache.h"
|
||||
#include <map>
|
||||
#include <cstdarg>
|
||||
|
||||
#include <coreinit/debug.h>
|
||||
#include <coreinit/memory.h>
|
||||
|
||||
#include <ter-u32b_bdf.h>
|
||||
#include <fa-solid-900_ttf.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
SDL_Window* window = nullptr;
|
||||
|
||||
SDL_Renderer* renderer = nullptr;
|
||||
|
||||
void* fontData = nullptr;
|
||||
|
||||
uint32_t fontSize = 0;
|
||||
|
||||
std::map<int, FC_Font*> fontMap;
|
||||
|
||||
FC_Font* monospaceFont = nullptr;
|
||||
|
||||
TTF_Font* iconFont = nullptr;
|
||||
|
||||
std::map<Uint16, SDL_Texture*> iconCache;
|
||||
|
||||
FC_Font* GetFontForSize(int size)
|
||||
{
|
||||
if (fontMap.contains(size)) {
|
||||
return fontMap[size];
|
||||
}
|
||||
|
||||
FC_Font* font = FC_CreateFont();
|
||||
if (!font) {
|
||||
return font;
|
||||
}
|
||||
|
||||
if (!FC_LoadFont_RW(font, renderer, SDL_RWFromMem(fontData, fontSize), 1, size, Gfx::COLOR_BLACK, TTF_STYLE_NORMAL)) {
|
||||
FC_FreeFont(font);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
fontMap.insert({size, font});
|
||||
return font;
|
||||
}
|
||||
|
||||
SDL_Texture* LoadIcon(Uint16 icon)
|
||||
{
|
||||
if (iconCache.contains(icon)) {
|
||||
return iconCache[icon];
|
||||
}
|
||||
|
||||
SDL_Surface* iconSurface = TTF_RenderGlyph_Blended(iconFont, icon, Gfx::COLOR_WHITE);
|
||||
if (!iconSurface) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, iconSurface);
|
||||
SDL_FreeSurface(iconSurface);
|
||||
if (!texture) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
iconCache.insert({icon, texture});
|
||||
return texture;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace Gfx
|
||||
{
|
||||
|
||||
bool Init()
|
||||
{
|
||||
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
window = SDL_CreateWindow("WiiUIdent", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, 0);
|
||||
if (!window) {
|
||||
OSReport("SDL_CreateWindow failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
|
||||
if (!renderer) {
|
||||
OSReport("SDL_CreateRenderer failed\n");
|
||||
SDL_DestroyWindow(window);
|
||||
window = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!OSGetSharedData(OS_SHAREDDATATYPE_FONT_STANDARD, 0, &fontData, &fontSize)) {
|
||||
OSReport("OSGetSharedData failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
TTF_Init();
|
||||
|
||||
monospaceFont = FC_CreateFont();
|
||||
if (!monospaceFont) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// FIXME Probably SDL bug? If we don't draw before FC_LoadFont_RW our viewport shrinks
|
||||
Gfx::DrawRectFilled(0, 0, 0, 0, COLOR_BLACK);
|
||||
|
||||
if (!FC_LoadFont_RW(monospaceFont, renderer, SDL_RWFromMem((void*)ter_u32b_bdf, ter_u32b_bdf_size), 1, 32, Gfx::COLOR_BLACK, TTF_STYLE_NORMAL)) {
|
||||
FC_FreeFont(monospaceFont);
|
||||
return false;
|
||||
}
|
||||
|
||||
// icons @256 should be large enough for our needs
|
||||
iconFont = TTF_OpenFontRW(SDL_RWFromMem((void*)fa_solid_900_ttf, fa_solid_900_ttf_size), 1, 256);
|
||||
if (!iconFont) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Shutdown()
|
||||
{
|
||||
for (const auto& [key, value] : fontMap) {
|
||||
FC_FreeFont(value);
|
||||
}
|
||||
|
||||
for (const auto& [key, value] : iconCache) {
|
||||
SDL_DestroyTexture(value);
|
||||
}
|
||||
|
||||
FC_FreeFont(monospaceFont);
|
||||
TTF_CloseFont(iconFont);
|
||||
TTF_Quit();
|
||||
SDL_DestroyRenderer(renderer);
|
||||
SDL_DestroyWindow(window);
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
void Clear(SDL_Color color)
|
||||
{
|
||||
SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a);
|
||||
SDL_RenderClear(renderer);
|
||||
}
|
||||
|
||||
void Render()
|
||||
{
|
||||
SDL_RenderPresent(renderer);
|
||||
}
|
||||
|
||||
void DrawRectFilled(int x, int y, int w, int h, SDL_Color color)
|
||||
{
|
||||
SDL_Rect rect{x, y, w, h};
|
||||
SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a);
|
||||
SDL_RenderFillRect(renderer, &rect);
|
||||
}
|
||||
|
||||
void DrawRect(int x, int y, int w, int h, int borderSize, SDL_Color color)
|
||||
{
|
||||
DrawRectFilled(x, y, w, borderSize, color);
|
||||
DrawRectFilled(x, y + h - borderSize, w, borderSize, color);
|
||||
DrawRectFilled(x, y, borderSize, h, color);
|
||||
DrawRectFilled(x + w - borderSize, y, borderSize, h, color);
|
||||
}
|
||||
|
||||
void DrawIcon(int x, int y, int size, SDL_Color color, Uint16 icon, AlignFlags align, double angle)
|
||||
{
|
||||
SDL_Texture* iconTex = LoadIcon(icon);
|
||||
if (!iconTex) {
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_SetTextureColorMod(iconTex, color.r, color.g, color.b);
|
||||
SDL_SetTextureAlphaMod(iconTex, color.a);
|
||||
|
||||
int w, h;
|
||||
SDL_QueryTexture(iconTex, nullptr, nullptr, &w, &h);
|
||||
|
||||
SDL_Rect rect;
|
||||
rect.x = x;
|
||||
rect.y = y;
|
||||
// scale the width based on hight to keep AR
|
||||
rect.w = (int) (((float) w / h) * size);
|
||||
rect.h = size;
|
||||
|
||||
if (align & ALIGN_RIGHT) {
|
||||
rect.x -= rect.w;
|
||||
} else if (align & ALIGN_HORIZONTAL) {
|
||||
rect.x -= rect.w / 2;
|
||||
}
|
||||
|
||||
if (align & ALIGN_BOTTOM) {
|
||||
rect.y -= rect.h;
|
||||
} else if (align & ALIGN_VERTICAL) {
|
||||
rect.y -= rect.h / 2;
|
||||
}
|
||||
|
||||
// draw the icon
|
||||
if (angle) {
|
||||
SDL_RenderCopyEx(renderer, iconTex, nullptr, &rect, angle, nullptr, SDL_FLIP_NONE);
|
||||
} else {
|
||||
SDL_RenderCopy(renderer, iconTex, nullptr, &rect);
|
||||
}
|
||||
}
|
||||
|
||||
int GetIconWidth(int size, Uint16 icon)
|
||||
{
|
||||
SDL_Texture* iconTex = LoadIcon(icon);
|
||||
if (!iconTex) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int w, h;
|
||||
SDL_QueryTexture(iconTex, nullptr, nullptr, &w, &h);
|
||||
|
||||
return (int) (((float) w / h) * size);
|
||||
}
|
||||
|
||||
void Print(int x, int y, int size, SDL_Color color, std::string text, AlignFlags align, bool monospace)
|
||||
{
|
||||
FC_Font* font = monospace ? monospaceFont : GetFontForSize(size);
|
||||
if (!font) {
|
||||
return;
|
||||
}
|
||||
|
||||
FC_Effect effect;
|
||||
effect.color = color;
|
||||
|
||||
// scale monospace font based on size
|
||||
if (monospace) {
|
||||
effect.scale = FC_MakeScale(size / 28.0f, size / 28.0f);
|
||||
// TODO figure out how to center this properly
|
||||
y += 5;
|
||||
} else {
|
||||
effect.scale = FC_MakeScale(1,1);
|
||||
}
|
||||
|
||||
if (align & ALIGN_LEFT) {
|
||||
effect.alignment = FC_ALIGN_LEFT;
|
||||
} else if (align & ALIGN_RIGHT) {
|
||||
effect.alignment = FC_ALIGN_RIGHT;
|
||||
} else if (align & ALIGN_HORIZONTAL) {
|
||||
effect.alignment = FC_ALIGN_CENTER;
|
||||
} else {
|
||||
// left by default
|
||||
effect.alignment = FC_ALIGN_LEFT;
|
||||
}
|
||||
|
||||
if (align & ALIGN_BOTTOM) {
|
||||
y -= GetTextHeight(size, text, monospace);
|
||||
} else if (align & ALIGN_VERTICAL) {
|
||||
y -= GetTextHeight(size, text, monospace) / 2;
|
||||
}
|
||||
|
||||
FC_DrawEffect(font, renderer, x, y, effect, "%s", text.c_str());
|
||||
}
|
||||
|
||||
int GetTextWidth(int size, std::string text, bool monospace)
|
||||
{
|
||||
FC_Font* font = monospace ? monospaceFont : GetFontForSize(size);
|
||||
if (!font) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
float scale = monospace ? (size / 28.0f) : 1.0f;
|
||||
|
||||
return FC_GetWidth(font, "%s", text.c_str()) * scale;
|
||||
}
|
||||
|
||||
int GetTextHeight(int size, std::string text, bool monospace)
|
||||
{
|
||||
// TODO this doesn't work nicely with monospace yet
|
||||
monospace = false;
|
||||
|
||||
FC_Font* font = monospace ? monospaceFont : GetFontForSize(size);
|
||||
if (!font) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
float scale = monospace ? (size / 28.0f) : 1.0f;
|
||||
|
||||
return FC_GetHeight(GetFontForSize(size), "%s", text.c_str()) * scale;
|
||||
}
|
||||
|
||||
}
|
||||
|
63
source/Gfx.hpp
Normal file
@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL.h>
|
||||
#include <string>
|
||||
|
||||
namespace Gfx
|
||||
{
|
||||
|
||||
constexpr uint32_t SCREEN_WIDTH = 1920;
|
||||
constexpr uint32_t SCREEN_HEIGHT = 1080;
|
||||
|
||||
constexpr SDL_Color COLOR_BLACK = { 0x00, 0x00, 0x00, 0xff };
|
||||
constexpr SDL_Color COLOR_WHITE = { 0xff, 0xff, 0xff, 0xff };
|
||||
constexpr SDL_Color COLOR_BACKGROUND = { 0x1b, 0x1c, 0x20, 0xff };
|
||||
constexpr SDL_Color COLOR_ALT_BACKGROUND = { 0x33, 0x34, 0x39, 0xff };
|
||||
constexpr SDL_Color COLOR_HIGHLIGHTED = { 0x00, 0x91, 0xea, 0xff };
|
||||
constexpr SDL_Color COLOR_TEXT = { 0xf8, 0xf8, 0xf8, 0xff };
|
||||
constexpr SDL_Color COLOR_ALT_TEXT = { 0xb0, 0xb0, 0xb0, 0xff };
|
||||
constexpr SDL_Color COLOR_ACCENT = { 0x32, 0xe6, 0xa6, 0xff };
|
||||
constexpr SDL_Color COLOR_ALT_ACCENT = { 0x22, 0xb3, 0x7d, 0xff };
|
||||
constexpr SDL_Color COLOR_BARS = { 0x2f, 0x3f, 0x38, 0xff };
|
||||
constexpr SDL_Color COLOR_ERROR = { 0xff, 0x33, 0x33, 0xff };
|
||||
constexpr SDL_Color COLOR_WIIU = { 0x00, 0x95, 0xc7, 0xff };
|
||||
|
||||
enum AlignFlags {
|
||||
ALIGN_LEFT = 1 << 0,
|
||||
ALIGN_RIGHT = 1 << 1,
|
||||
ALIGN_HORIZONTAL = 1 << 2,
|
||||
ALIGN_TOP = 1 << 3,
|
||||
ALIGN_BOTTOM = 1 << 4,
|
||||
ALIGN_VERTICAL = 1 << 5,
|
||||
ALIGN_CENTER = ALIGN_HORIZONTAL | ALIGN_VERTICAL,
|
||||
};
|
||||
|
||||
static constexpr inline AlignFlags operator|(AlignFlags lhs, AlignFlags rhs) {
|
||||
return static_cast<AlignFlags>(static_cast<uint32_t>(lhs) | static_cast<uint32_t>(rhs));
|
||||
}
|
||||
|
||||
bool Init();
|
||||
|
||||
void Shutdown();
|
||||
|
||||
void Clear(SDL_Color color);
|
||||
|
||||
void Render();
|
||||
|
||||
void DrawRectFilled(int x, int y, int w, int h, SDL_Color color);
|
||||
|
||||
void DrawRect(int x, int y, int w, int h, int borderSize, SDL_Color color);
|
||||
|
||||
void DrawIcon(int x, int y, int size, SDL_Color color, Uint16 icon, AlignFlags align = ALIGN_CENTER, double angle = 0.0);
|
||||
|
||||
int GetIconWidth(int size, Uint16 icon);
|
||||
|
||||
static inline int GetIconHeight(int size, Uint16 icon) { return size; }
|
||||
|
||||
void Print(int x, int y, int size, SDL_Color color, std::string text, AlignFlags align = ALIGN_LEFT | ALIGN_TOP, bool monospace = false);
|
||||
|
||||
int GetTextWidth(int size, std::string text, bool monospace = false);
|
||||
|
||||
int GetTextHeight(int size, std::string text, bool monospace = false);
|
||||
|
||||
}
|
2925
source/SDL_FontCache.c
Normal file
327
source/SDL_FontCache.h
Normal file
@ -0,0 +1,327 @@
|
||||
/*
|
||||
SDL_FontCache v0.10.0: A font cache for SDL and SDL_ttf
|
||||
by Jonathan Dearborn
|
||||
Dedicated to the memory of Florian Hufsky
|
||||
|
||||
License:
|
||||
The short:
|
||||
Use it however you'd like, but keep the copyright and license notice
|
||||
whenever these files or parts of them are distributed in uncompiled form.
|
||||
|
||||
The long:
|
||||
Copyright (c) 2019 Jonathan Dearborn
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _SDL_FONTCACHE_H__
|
||||
#define _SDL_FONTCACHE_H__
|
||||
|
||||
#include "SDL.h"
|
||||
#include "SDL_ttf.h"
|
||||
|
||||
#ifdef FC_USE_SDL_GPU
|
||||
#include "SDL_gpu.h"
|
||||
#endif
|
||||
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
// Let's pretend this exists...
|
||||
#define TTF_STYLE_OUTLINE 16
|
||||
|
||||
|
||||
|
||||
// Differences between SDL_Renderer and SDL_gpu
|
||||
#ifdef FC_USE_SDL_GPU
|
||||
#define FC_Rect GPU_Rect
|
||||
#define FC_Target GPU_Target
|
||||
#define FC_Image GPU_Image
|
||||
#define FC_Log GPU_LogError
|
||||
#else
|
||||
#define FC_Rect SDL_Rect
|
||||
#define FC_Target SDL_Renderer
|
||||
#define FC_Image SDL_Texture
|
||||
#define FC_Log SDL_Log
|
||||
#endif
|
||||
|
||||
|
||||
// SDL_FontCache types
|
||||
|
||||
typedef enum
|
||||
{
|
||||
FC_ALIGN_LEFT,
|
||||
FC_ALIGN_CENTER,
|
||||
FC_ALIGN_RIGHT
|
||||
} FC_AlignEnum;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
FC_FILTER_NEAREST,
|
||||
FC_FILTER_LINEAR
|
||||
} FC_FilterEnum;
|
||||
|
||||
typedef struct FC_Scale
|
||||
{
|
||||
float x;
|
||||
float y;
|
||||
|
||||
} FC_Scale;
|
||||
|
||||
typedef struct FC_Effect
|
||||
{
|
||||
FC_AlignEnum alignment;
|
||||
FC_Scale scale;
|
||||
SDL_Color color;
|
||||
|
||||
} FC_Effect;
|
||||
|
||||
// Opaque type
|
||||
typedef struct FC_Font FC_Font;
|
||||
|
||||
|
||||
typedef struct FC_GlyphData
|
||||
{
|
||||
SDL_Rect rect;
|
||||
int cache_level;
|
||||
|
||||
} FC_GlyphData;
|
||||
|
||||
|
||||
|
||||
|
||||
// Object creation
|
||||
|
||||
FC_Rect FC_MakeRect(float x, float y, float w, float h);
|
||||
|
||||
FC_Scale FC_MakeScale(float x, float y);
|
||||
|
||||
SDL_Color FC_MakeColor(Uint8 r, Uint8 g, Uint8 b, Uint8 a);
|
||||
|
||||
FC_Effect FC_MakeEffect(FC_AlignEnum alignment, FC_Scale scale, SDL_Color color);
|
||||
|
||||
FC_GlyphData FC_MakeGlyphData(int cache_level, Sint16 x, Sint16 y, Uint16 w, Uint16 h);
|
||||
|
||||
|
||||
|
||||
// Font object
|
||||
|
||||
FC_Font* FC_CreateFont(void);
|
||||
|
||||
#ifdef FC_USE_SDL_GPU
|
||||
Uint8 FC_LoadFont(FC_Font* font, const char* filename_ttf, Uint32 pointSize, SDL_Color color, int style);
|
||||
|
||||
Uint8 FC_LoadFontFromTTF(FC_Font* font, TTF_Font* ttf, SDL_Color color);
|
||||
|
||||
Uint8 FC_LoadFont_RW(FC_Font* font, SDL_RWops* file_rwops_ttf, Uint8 own_rwops, Uint32 pointSize, SDL_Color color, int style);
|
||||
#else
|
||||
Uint8 FC_LoadFont(FC_Font* font, SDL_Renderer* renderer, const char* filename_ttf, Uint32 pointSize, SDL_Color color, int style);
|
||||
|
||||
Uint8 FC_LoadFontFromTTF(FC_Font* font, SDL_Renderer* renderer, TTF_Font* ttf, SDL_Color color);
|
||||
|
||||
Uint8 FC_LoadFont_RW(FC_Font* font, SDL_Renderer* renderer, SDL_RWops* file_rwops_ttf, Uint8 own_rwops, Uint32 pointSize, SDL_Color color, int style);
|
||||
#endif
|
||||
|
||||
#ifndef FC_USE_SDL_GPU
|
||||
// note: handle SDL event types SDL_RENDER_TARGETS_RESET(>= SDL 2.0.2) and SDL_RENDER_DEVICE_RESET(>= SDL 2.0.4)
|
||||
void FC_ResetFontFromRendererReset(FC_Font* font, SDL_Renderer* renderer, Uint32 evType);
|
||||
#endif
|
||||
|
||||
void FC_ClearFont(FC_Font* font);
|
||||
|
||||
void FC_FreeFont(FC_Font* font);
|
||||
|
||||
|
||||
|
||||
// Built-in loading strings
|
||||
|
||||
char* FC_GetStringASCII(void);
|
||||
|
||||
char* FC_GetStringLatin1(void);
|
||||
|
||||
char* FC_GetStringASCII_Latin1(void);
|
||||
|
||||
|
||||
// UTF-8 to SDL_FontCache codepoint conversion
|
||||
|
||||
/*!
|
||||
Returns the Uint32 codepoint (not UTF-32) parsed from the given UTF-8 string.
|
||||
\param c A pointer to a string of proper UTF-8 character values.
|
||||
\param advance_pointer If true, the source pointer will be incremented to skip the extra bytes from multibyte codepoints.
|
||||
*/
|
||||
Uint32 FC_GetCodepointFromUTF8(const char** c, Uint8 advance_pointer);
|
||||
|
||||
/*!
|
||||
Parses the given codepoint and stores the UTF-8 bytes in 'result'. The result is NULL terminated.
|
||||
\param result A memory buffer for the UTF-8 values. Must be at least 5 bytes long.
|
||||
\param codepoint The Uint32 codepoint to parse (not UTF-32).
|
||||
*/
|
||||
void FC_GetUTF8FromCodepoint(char* result, Uint32 codepoint);
|
||||
|
||||
|
||||
// UTF-8 string operations
|
||||
|
||||
/*! Allocates a new string of 'size' bytes that is already NULL-terminated. The NULL byte counts toward the size limit, as usual. Returns NULL if size is 0. */
|
||||
char* U8_alloc(unsigned int size);
|
||||
|
||||
/*! Deallocates the given string. */
|
||||
void U8_free(char* string);
|
||||
|
||||
/*! Allocates a copy of the given string. */
|
||||
char* U8_strdup(const char* string);
|
||||
|
||||
/*! Returns the number of UTF-8 characters in the given string. */
|
||||
int U8_strlen(const char* string);
|
||||
|
||||
/*! Returns the number of bytes in the UTF-8 multibyte character pointed at by 'character'. */
|
||||
int U8_charsize(const char* character);
|
||||
|
||||
/*! Copies the source multibyte character into the given buffer without overrunning it. Returns 0 on failure. */
|
||||
int U8_charcpy(char* buffer, const char* source, int buffer_size);
|
||||
|
||||
/*! Returns a pointer to the next UTF-8 character. */
|
||||
const char* U8_next(const char* string);
|
||||
|
||||
/*! Inserts a UTF-8 string into 'string' at the given position. Use a position of -1 to append. Returns 0 when unable to insert the string. */
|
||||
int U8_strinsert(char* string, int position, const char* source, int max_bytes);
|
||||
|
||||
/*! Erases the UTF-8 character at the given position, moving the subsequent characters down. */
|
||||
void U8_strdel(char* string, int position);
|
||||
|
||||
|
||||
// Internal settings
|
||||
|
||||
/*! Sets the string from which to load the initial glyphs. Use this if you need upfront loading for any reason (such as lack of render-target support). */
|
||||
void FC_SetLoadingString(FC_Font* font, const char* string);
|
||||
|
||||
/*! Returns the size of the internal buffer which is used for unpacking variadic text data. This buffer is shared by all FC_Fonts. */
|
||||
unsigned int FC_GetBufferSize(void);
|
||||
|
||||
/*! Changes the size of the internal buffer which is used for unpacking variadic text data. This buffer is shared by all FC_Fonts. */
|
||||
void FC_SetBufferSize(unsigned int size);
|
||||
|
||||
/*! Returns the width of a single horizontal tab in multiples of the width of a space (default: 4) */
|
||||
unsigned int FC_GetTabWidth(void);
|
||||
|
||||
/*! Changes the width of a horizontal tab in multiples of the width of a space (default: 4) */
|
||||
void FC_SetTabWidth(unsigned int width_in_spaces);
|
||||
|
||||
void FC_SetRenderCallback(FC_Rect (*callback)(FC_Image* src, FC_Rect* srcrect, FC_Target* dest, float x, float y, float xscale, float yscale));
|
||||
|
||||
FC_Rect FC_DefaultRenderCallback(FC_Image* src, FC_Rect* srcrect, FC_Target* dest, float x, float y, float xscale, float yscale);
|
||||
|
||||
|
||||
// Custom caching
|
||||
|
||||
/*! Returns the number of cache levels that are active. */
|
||||
int FC_GetNumCacheLevels(FC_Font* font);
|
||||
|
||||
/*! Returns the cache source texture at the given cache level. */
|
||||
FC_Image* FC_GetGlyphCacheLevel(FC_Font* font, int cache_level);
|
||||
|
||||
// TODO: Specify ownership of the texture (should be shareable)
|
||||
/*! Sets a cache source texture for rendering. New cache levels must be sequential. */
|
||||
Uint8 FC_SetGlyphCacheLevel(FC_Font* font, int cache_level, FC_Image* cache_texture);
|
||||
|
||||
/*! Copies the given surface to the given cache level as a texture. New cache levels must be sequential. */
|
||||
Uint8 FC_UploadGlyphCache(FC_Font* font, int cache_level, SDL_Surface* data_surface);
|
||||
|
||||
|
||||
/*! Returns the number of codepoints that are stored in the font's glyph data map. */
|
||||
unsigned int FC_GetNumCodepoints(FC_Font* font);
|
||||
|
||||
/*! Copies the stored codepoints into the given array. */
|
||||
void FC_GetCodepoints(FC_Font* font, Uint32* result);
|
||||
|
||||
/*! Stores the glyph data for the given codepoint in 'result'. Returns 0 if the codepoint was not found in the cache. */
|
||||
Uint8 FC_GetGlyphData(FC_Font* font, FC_GlyphData* result, Uint32 codepoint);
|
||||
|
||||
/*! Sets the glyph data for the given codepoint. Duplicates are not checked. Returns a pointer to the stored data. */
|
||||
FC_GlyphData* FC_SetGlyphData(FC_Font* font, Uint32 codepoint, FC_GlyphData glyph_data);
|
||||
|
||||
|
||||
// Rendering
|
||||
|
||||
FC_Rect FC_Draw(FC_Font* font, FC_Target* dest, float x, float y, const char* formatted_text, ...);
|
||||
FC_Rect FC_DrawAlign(FC_Font* font, FC_Target* dest, float x, float y, FC_AlignEnum align, const char* formatted_text, ...);
|
||||
FC_Rect FC_DrawScale(FC_Font* font, FC_Target* dest, float x, float y, FC_Scale scale, const char* formatted_text, ...);
|
||||
FC_Rect FC_DrawColor(FC_Font* font, FC_Target* dest, float x, float y, SDL_Color color, const char* formatted_text, ...);
|
||||
FC_Rect FC_DrawEffect(FC_Font* font, FC_Target* dest, float x, float y, FC_Effect effect, const char* formatted_text, ...);
|
||||
|
||||
FC_Rect FC_DrawBox(FC_Font* font, FC_Target* dest, FC_Rect box, const char* formatted_text, ...);
|
||||
FC_Rect FC_DrawBoxAlign(FC_Font* font, FC_Target* dest, FC_Rect box, FC_AlignEnum align, const char* formatted_text, ...);
|
||||
FC_Rect FC_DrawBoxScale(FC_Font* font, FC_Target* dest, FC_Rect box, FC_Scale scale, const char* formatted_text, ...);
|
||||
FC_Rect FC_DrawBoxColor(FC_Font* font, FC_Target* dest, FC_Rect box, SDL_Color color, const char* formatted_text, ...);
|
||||
FC_Rect FC_DrawBoxEffect(FC_Font* font, FC_Target* dest, FC_Rect box, FC_Effect effect, const char* formatted_text, ...);
|
||||
|
||||
FC_Rect FC_DrawColumn(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, const char* formatted_text, ...);
|
||||
FC_Rect FC_DrawColumnAlign(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, FC_AlignEnum align, const char* formatted_text, ...);
|
||||
FC_Rect FC_DrawColumnScale(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, FC_Scale scale, const char* formatted_text, ...);
|
||||
FC_Rect FC_DrawColumnColor(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, SDL_Color color, const char* formatted_text, ...);
|
||||
FC_Rect FC_DrawColumnEffect(FC_Font* font, FC_Target* dest, float x, float y, Uint16 width, FC_Effect effect, const char* formatted_text, ...);
|
||||
|
||||
|
||||
// Getters
|
||||
|
||||
FC_FilterEnum FC_GetFilterMode(FC_Font* font);
|
||||
Uint16 FC_GetLineHeight(FC_Font* font);
|
||||
Uint16 FC_GetHeight(FC_Font* font, const char* formatted_text, ...);
|
||||
Uint16 FC_GetWidth(FC_Font* font, const char* formatted_text, ...);
|
||||
|
||||
// Returns a 1-pixel wide box in front of the character in the given position (index)
|
||||
FC_Rect FC_GetCharacterOffset(FC_Font* font, Uint16 position_index, int column_width, const char* formatted_text, ...);
|
||||
Uint16 FC_GetColumnHeight(FC_Font* font, Uint16 width, const char* formatted_text, ...);
|
||||
|
||||
int FC_GetAscent(FC_Font* font, const char* formatted_text, ...);
|
||||
int FC_GetDescent(FC_Font* font, const char* formatted_text, ...);
|
||||
int FC_GetBaseline(FC_Font* font);
|
||||
int FC_GetSpacing(FC_Font* font);
|
||||
int FC_GetLineSpacing(FC_Font* font);
|
||||
Uint16 FC_GetMaxWidth(FC_Font* font);
|
||||
SDL_Color FC_GetDefaultColor(FC_Font* font);
|
||||
|
||||
FC_Rect FC_GetBounds(FC_Font* font, float x, float y, FC_AlignEnum align, FC_Scale scale, const char* formatted_text, ...);
|
||||
|
||||
Uint8 FC_InRect(float x, float y, FC_Rect input_rect);
|
||||
// Given an offset (x,y) from the text draw position (the upper-left corner), returns the character position (UTF-8 index)
|
||||
Uint16 FC_GetPositionFromOffset(FC_Font* font, float x, float y, int column_width, FC_AlignEnum align, const char* formatted_text, ...);
|
||||
|
||||
// Returns the number of characters in the new wrapped text written into `result`.
|
||||
int FC_GetWrappedText(FC_Font* font, char* result, int max_result_size, Uint16 width, const char* formatted_text, ...);
|
||||
|
||||
// Setters
|
||||
|
||||
void FC_SetFilterMode(FC_Font* font, FC_FilterEnum filter);
|
||||
void FC_SetSpacing(FC_Font* font, int LetterSpacing);
|
||||
void FC_SetLineSpacing(FC_Font* font, int LineSpacing);
|
||||
void FC_SetDefaultColor(FC_Font* font, SDL_Color color);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#endif
|
54
source/Screen.cpp
Normal file
@ -0,0 +1,54 @@
|
||||
#include "Screen.hpp"
|
||||
#include "Gfx.hpp"
|
||||
|
||||
void Screen::DrawTopBar(const char* name)
|
||||
{
|
||||
// draw top bar
|
||||
Gfx::DrawRectFilled(0, 0, Gfx::SCREEN_WIDTH, 75, Gfx::COLOR_BARS);
|
||||
|
||||
// draw top bar content
|
||||
Gfx::DrawIcon(32, 75 / 2, 60, Gfx::COLOR_TEXT, 0xf002, Gfx::ALIGN_VERTICAL);
|
||||
Gfx::Print(128, 75 / 2, 60, Gfx::COLOR_TEXT, "WiiUIdent", Gfx::ALIGN_VERTICAL);
|
||||
Gfx::Print(Gfx::GetTextWidth(60, "WiiUIdent") + 128 + 16, 75 / 2 + 5, 50, Gfx::COLOR_ALT_TEXT, "v" APP_VERSION, Gfx::ALIGN_VERTICAL);
|
||||
if (name)
|
||||
Gfx::Print(Gfx::SCREEN_WIDTH - 32, 75 / 2, 50, Gfx::COLOR_ALT_TEXT, name, Gfx::ALIGN_VERTICAL | Gfx::ALIGN_RIGHT);
|
||||
}
|
||||
|
||||
void Screen::DrawBottomBar(const char* leftHint, const char* centerHint, const char* rightHint)
|
||||
{
|
||||
// draw bottom bar
|
||||
Gfx::DrawRectFilled(0, Gfx::SCREEN_HEIGHT - 75, Gfx::SCREEN_WIDTH, 75, Gfx::COLOR_BARS);
|
||||
|
||||
// draw bottom bar content
|
||||
if (leftHint)
|
||||
Gfx::Print(32, Gfx::SCREEN_HEIGHT - 75 / 2, 50, Gfx::COLOR_TEXT, leftHint, Gfx::ALIGN_VERTICAL);
|
||||
if (centerHint)
|
||||
Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT - 75 / 2, 50, Gfx::COLOR_TEXT, centerHint, Gfx::ALIGN_CENTER);
|
||||
if (rightHint)
|
||||
Gfx::Print(Gfx::SCREEN_WIDTH - 32, Gfx::SCREEN_HEIGHT - 78 / 2, 50, Gfx::COLOR_TEXT, rightHint, Gfx::ALIGN_VERTICAL | Gfx::ALIGN_RIGHT);
|
||||
}
|
||||
|
||||
int Screen::DrawHeader(int x, int y, int w, uint16_t icon, const char* text)
|
||||
{
|
||||
const int iconWidth = Gfx::GetIconWidth(50, icon);
|
||||
const int width = iconWidth + 32 + Gfx::GetTextWidth(50, text);
|
||||
const int xStart = x + (w / 2) - (width / 2);
|
||||
|
||||
Gfx::DrawIcon(xStart, y, 50, Gfx::COLOR_TEXT, icon, Gfx::ALIGN_VERTICAL);
|
||||
Gfx::Print(xStart + iconWidth + 32, y, 50, Gfx::COLOR_TEXT, text, Gfx::ALIGN_VERTICAL);
|
||||
Gfx::DrawRectFilled(x, y + 32, w, 4, Gfx::COLOR_ACCENT);
|
||||
|
||||
return y + 64;
|
||||
}
|
||||
|
||||
int Screen::DrawList(int x, int y, int w, ScreenList items)
|
||||
{
|
||||
int yOff = y;
|
||||
for (const auto& item : items) {
|
||||
Gfx::Print(x + 16, yOff, 40, Gfx::COLOR_TEXT, item.first, Gfx::ALIGN_VERTICAL);
|
||||
Gfx::Print(x + w - 16, yOff, 40, Gfx::COLOR_TEXT, item.second.string, Gfx::ALIGN_VERTICAL | Gfx::ALIGN_RIGHT, item.second.monospace);
|
||||
yOff += std::max(Gfx::GetTextHeight(40, item.first), Gfx::GetTextHeight(40, item.second.string, item.second.monospace));
|
||||
}
|
||||
|
||||
return yOff + 32;
|
||||
}
|
40
source/Screen.hpp
Normal file
@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <vpad/input.h>
|
||||
|
||||
class Screen
|
||||
{
|
||||
public:
|
||||
Screen() = default;
|
||||
virtual ~Screen() = default;
|
||||
|
||||
virtual void Draw() = 0;
|
||||
|
||||
virtual bool Update(VPADStatus& input) = 0;
|
||||
|
||||
protected:
|
||||
void DrawTopBar(const char* name);
|
||||
|
||||
void DrawBottomBar(const char* leftHint, const char* centerHint, const char* rightHint);
|
||||
|
||||
int DrawHeader(int x, int y, int w, uint16_t icon, const char* text);
|
||||
|
||||
struct ScreenListElement
|
||||
{
|
||||
ScreenListElement(std::string string, bool monospace = false)
|
||||
: string(string), monospace(monospace) {}
|
||||
ScreenListElement(const char* string, bool monospace = false)
|
||||
: string(string), monospace(monospace) {}
|
||||
|
||||
std::string string;
|
||||
bool monospace;
|
||||
};
|
||||
using ScreenList = std::vector<std::pair<std::string, ScreenListElement>>;
|
||||
|
||||
int DrawList(int x, int y, int w, ScreenList items);
|
||||
|
||||
private:
|
||||
};
|
67
source/Utils.cpp
Normal file
@ -0,0 +1,67 @@
|
||||
#include "Utils.hpp"
|
||||
#include <cstring>
|
||||
|
||||
#include <mbedtls/aes.h>
|
||||
#include <mbedtls/sha256.h>
|
||||
|
||||
namespace Utils
|
||||
{
|
||||
|
||||
std::string ToHexString(const void* data, size_t size)
|
||||
{
|
||||
std::string str;
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
str += Utils::sprintf("%02x", ((const uint8_t*) data)[i]);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
int AESDecrypt(const void* key, unsigned keybits, const void* iv, const void* in, void* out, size_t dataSize)
|
||||
{
|
||||
int res;
|
||||
mbedtls_aes_context ctx;
|
||||
mbedtls_aes_init(&ctx);
|
||||
|
||||
if ((res = mbedtls_aes_setkey_dec(&ctx, (const unsigned char*)key, keybits)) != 0) {
|
||||
mbedtls_aes_free(&ctx);
|
||||
return res;
|
||||
}
|
||||
|
||||
unsigned char tmpiv[16];
|
||||
if (iv) {
|
||||
memcpy(tmpiv, iv, sizeof(tmpiv));
|
||||
} else {
|
||||
memset(tmpiv, 0, sizeof(tmpiv));
|
||||
}
|
||||
|
||||
res = mbedtls_aes_crypt_cbc(&ctx, MBEDTLS_AES_DECRYPT, dataSize, tmpiv, (const unsigned char*)in, (unsigned char*)out);
|
||||
mbedtls_aes_free(&ctx);
|
||||
return res;
|
||||
}
|
||||
|
||||
int SHA256(const void* in, size_t inSize, void* out, size_t outSize)
|
||||
{
|
||||
if (outSize != 32) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int res;
|
||||
mbedtls_sha256_context ctx;
|
||||
mbedtls_sha256_init(&ctx);
|
||||
|
||||
if ((res = mbedtls_sha256_starts_ret(&ctx, 0)) != 0) {
|
||||
mbedtls_sha256_free(&ctx);
|
||||
return res;
|
||||
}
|
||||
|
||||
if ((res = mbedtls_sha256_update_ret(&ctx, (const unsigned char*)in, inSize)) != 0) {
|
||||
mbedtls_sha256_free(&ctx);
|
||||
return res;
|
||||
}
|
||||
|
||||
res = mbedtls_sha256_finish_ret(&ctx, (unsigned char*)out);
|
||||
mbedtls_sha256_free(&ctx);
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
26
source/Utils.hpp
Normal file
@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
namespace Utils
|
||||
{
|
||||
|
||||
template<typename ...Args>
|
||||
std::string sprintf(const std::string& format, Args ...args)
|
||||
{
|
||||
int size = std::snprintf(nullptr, 0, format.c_str(), args ...) + 1;
|
||||
|
||||
std::unique_ptr<char[]> buf(new char[size]);
|
||||
std::snprintf(buf.get(), size, format.c_str(), args ...);
|
||||
|
||||
return std::string(buf.get(), buf.get() + size - 1);
|
||||
}
|
||||
|
||||
std::string ToHexString(const void* data, size_t size);
|
||||
|
||||
int AESDecrypt(const void* key, uint32_t keybits, const void* iv, const void* in, void* out, size_t dataSize);
|
||||
|
||||
int SHA256(const void* in, size_t inSize, void* out, size_t outSize);
|
||||
|
||||
}
|
166
source/main.cpp
Normal file
@ -0,0 +1,166 @@
|
||||
#include "Gfx.hpp"
|
||||
#include "screens/MainScreen.hpp"
|
||||
#include <memory>
|
||||
|
||||
#include <whb/proc.h>
|
||||
#include <coreinit/title.h>
|
||||
#include <sysapp/launch.h>
|
||||
#include <vpad/input.h>
|
||||
#include <padscore/kpad.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
inline bool RunningFromMiiMaker()
|
||||
{
|
||||
return (OSGetTitleID() & 0xFFFFFFFFFFFFF0FFull) == 0x000500101004A000ull;
|
||||
}
|
||||
|
||||
|
||||
uint32_t RemapWiiMoteButtons(uint32_t buttons)
|
||||
{
|
||||
uint32_t conv_buttons = 0;
|
||||
|
||||
if (buttons & WPAD_BUTTON_LEFT)
|
||||
conv_buttons |= VPAD_BUTTON_LEFT;
|
||||
|
||||
if (buttons & WPAD_BUTTON_RIGHT)
|
||||
conv_buttons |= VPAD_BUTTON_RIGHT;
|
||||
|
||||
if (buttons & WPAD_BUTTON_DOWN)
|
||||
conv_buttons |= VPAD_BUTTON_DOWN;
|
||||
|
||||
if (buttons & WPAD_BUTTON_UP)
|
||||
conv_buttons |= VPAD_BUTTON_UP;
|
||||
|
||||
if (buttons & WPAD_BUTTON_PLUS)
|
||||
conv_buttons |= VPAD_BUTTON_PLUS;
|
||||
|
||||
if (buttons & WPAD_BUTTON_B)
|
||||
conv_buttons |= VPAD_BUTTON_B;
|
||||
|
||||
if (buttons & WPAD_BUTTON_A)
|
||||
conv_buttons |= VPAD_BUTTON_A;
|
||||
|
||||
if (buttons & WPAD_BUTTON_MINUS)
|
||||
conv_buttons |= VPAD_BUTTON_MINUS;
|
||||
|
||||
if (buttons & WPAD_BUTTON_HOME)
|
||||
conv_buttons |= VPAD_BUTTON_HOME;
|
||||
|
||||
return conv_buttons;
|
||||
}
|
||||
|
||||
uint32_t RemapClassicButtons(uint32_t buttons)
|
||||
{
|
||||
uint32_t conv_buttons = 0;
|
||||
|
||||
if (buttons & WPAD_CLASSIC_BUTTON_LEFT)
|
||||
conv_buttons |= VPAD_BUTTON_LEFT;
|
||||
|
||||
if (buttons & WPAD_CLASSIC_BUTTON_RIGHT)
|
||||
conv_buttons |= VPAD_BUTTON_RIGHT;
|
||||
|
||||
if (buttons & WPAD_CLASSIC_BUTTON_DOWN)
|
||||
conv_buttons |= VPAD_BUTTON_DOWN;
|
||||
|
||||
if (buttons & WPAD_CLASSIC_BUTTON_UP)
|
||||
conv_buttons |= VPAD_BUTTON_UP;
|
||||
|
||||
if (buttons & WPAD_CLASSIC_BUTTON_PLUS)
|
||||
conv_buttons |= VPAD_BUTTON_PLUS;
|
||||
|
||||
if (buttons & WPAD_CLASSIC_BUTTON_X)
|
||||
conv_buttons |= VPAD_BUTTON_X;
|
||||
|
||||
if (buttons & WPAD_CLASSIC_BUTTON_Y)
|
||||
conv_buttons |= VPAD_BUTTON_Y;
|
||||
|
||||
if (buttons & WPAD_CLASSIC_BUTTON_B)
|
||||
conv_buttons |= VPAD_BUTTON_B;
|
||||
|
||||
if (buttons & WPAD_CLASSIC_BUTTON_A)
|
||||
conv_buttons |= VPAD_BUTTON_A;
|
||||
|
||||
if (buttons & WPAD_CLASSIC_BUTTON_MINUS)
|
||||
conv_buttons |= VPAD_BUTTON_MINUS;
|
||||
|
||||
if (buttons & WPAD_CLASSIC_BUTTON_HOME)
|
||||
conv_buttons |= VPAD_BUTTON_HOME;
|
||||
|
||||
if (buttons & WPAD_CLASSIC_BUTTON_ZR)
|
||||
conv_buttons |= VPAD_BUTTON_ZR;
|
||||
|
||||
if (buttons & WPAD_CLASSIC_BUTTON_ZL)
|
||||
conv_buttons |= VPAD_BUTTON_ZL;
|
||||
|
||||
if (buttons & WPAD_CLASSIC_BUTTON_R)
|
||||
conv_buttons |= VPAD_BUTTON_R;
|
||||
|
||||
if (buttons & WPAD_CLASSIC_BUTTON_L)
|
||||
conv_buttons |= VPAD_BUTTON_L;
|
||||
|
||||
return conv_buttons;
|
||||
}
|
||||
|
||||
void UpdatePads(VPADStatus* status)
|
||||
{
|
||||
KPADStatus kpad_data{};
|
||||
KPADError kpad_error;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (KPADReadEx((KPADChan) i, &kpad_data, 1, &kpad_error) > 0) {
|
||||
if (kpad_error == KPAD_ERROR_OK && kpad_data.extensionType != 0xFF) {
|
||||
if (kpad_data.extensionType == WPAD_EXT_CORE || kpad_data.extensionType == WPAD_EXT_NUNCHUK) {
|
||||
status->trigger |= RemapWiiMoteButtons(kpad_data.trigger);
|
||||
status->release |= RemapWiiMoteButtons(kpad_data.release);
|
||||
status->hold |= RemapWiiMoteButtons(kpad_data.hold);
|
||||
} else {
|
||||
status->trigger |= RemapClassicButtons(kpad_data.classic.trigger);
|
||||
status->release |= RemapClassicButtons(kpad_data.classic.release);
|
||||
status->hold |= RemapClassicButtons(kpad_data.classic.hold);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char const* argv[])
|
||||
{
|
||||
WHBProcInit();
|
||||
|
||||
KPADInit();
|
||||
WPADEnableURCC(TRUE);
|
||||
|
||||
Gfx::Init();
|
||||
|
||||
std::unique_ptr<Screen> mainScreen = std::make_unique<MainScreen>();
|
||||
|
||||
while (WHBProcIsRunning()) {
|
||||
VPADStatus input{};
|
||||
VPADRead(VPAD_CHAN_0, &input, 1, nullptr);
|
||||
UpdatePads(&input);
|
||||
|
||||
if (!mainScreen->Update(input)) {
|
||||
// screen requested quit
|
||||
if (RunningFromMiiMaker()) {
|
||||
// legacy way, just quit
|
||||
break;
|
||||
} else {
|
||||
// launch menu otherwise
|
||||
SYSLaunchMenu();
|
||||
}
|
||||
}
|
||||
|
||||
mainScreen->Draw();
|
||||
Gfx::Render();
|
||||
}
|
||||
|
||||
mainScreen.reset();
|
||||
|
||||
Gfx::Shutdown();
|
||||
|
||||
WHBProcShutdown();
|
||||
return 0;
|
||||
}
|
45
source/screens/AboutScreen.cpp
Normal file
@ -0,0 +1,45 @@
|
||||
#include "AboutScreen.hpp"
|
||||
|
||||
AboutScreen::AboutScreen()
|
||||
{
|
||||
creditList.push_back({"Developers:", "GaryOderNichts"});
|
||||
creditList.push_back({"", "GerbilSoft"});
|
||||
|
||||
fontList.push_back({"Main Font:", "Wii U System Font"});
|
||||
fontList.push_back({"Icon Font:", "FontAwesome"});
|
||||
fontList.push_back({"Monospace Font:", "Terminus Font"});
|
||||
|
||||
linkList.push_back({"GitHub:", ""});
|
||||
linkList.push_back({"", {"github.com/GaryOderNichts/WiiUIdent", true}});
|
||||
linkList.push_back({"System Database:", {DATABASE_URL, true}});
|
||||
}
|
||||
|
||||
AboutScreen::~AboutScreen()
|
||||
{
|
||||
}
|
||||
|
||||
void AboutScreen::Draw()
|
||||
{
|
||||
DrawTopBar("About");
|
||||
|
||||
int yOff = 128;
|
||||
yOff = DrawHeader(32, yOff, 896, 0xf121, "Credits");
|
||||
yOff = DrawList(32, yOff, 896, creditList);
|
||||
yOff = DrawHeader(32, yOff, 896, 0xf031, "Fonts");
|
||||
yOff = DrawList(32, yOff, 896, fontList);
|
||||
|
||||
yOff = 128;
|
||||
yOff = DrawHeader(992, yOff, 896, 0xf08e, "Links");
|
||||
yOff = DrawList(992, yOff, 896, linkList);
|
||||
|
||||
DrawBottomBar(nullptr, "\ue044 Exit", "\ue001 Back");
|
||||
}
|
||||
|
||||
bool AboutScreen::Update(VPADStatus& input)
|
||||
{
|
||||
if (input.trigger & VPAD_BUTTON_B) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
19
source/screens/AboutScreen.hpp
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "Screen.hpp"
|
||||
|
||||
class AboutScreen : public Screen
|
||||
{
|
||||
public:
|
||||
AboutScreen();
|
||||
virtual ~AboutScreen();
|
||||
|
||||
void Draw();
|
||||
|
||||
bool Update(VPADStatus& input);
|
||||
|
||||
private:
|
||||
ScreenList creditList;
|
||||
ScreenList fontList;
|
||||
ScreenList linkList;
|
||||
};
|
182
source/screens/GeneralScreen.cpp
Normal file
@ -0,0 +1,182 @@
|
||||
#include "GeneralScreen.hpp"
|
||||
#include "Gfx.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "system/OTP.hpp"
|
||||
#include "system/SEEPROM.hpp"
|
||||
#include <map>
|
||||
|
||||
#include <coreinit/mcp.h>
|
||||
#include <coreinit/bsp.h>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
typedef struct {
|
||||
uint32_t major;
|
||||
uint32_t minor;
|
||||
uint32_t patch;
|
||||
char region;
|
||||
} MCPSystemVersion;
|
||||
|
||||
MCPError MCP_GetSystemVersion(int32_t handle, MCPSystemVersion* systemVersion);
|
||||
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
static const char* const consoleTypeLookup[] = {
|
||||
"Invalid", "WUP", "CAT-R", "CAT-DEV",
|
||||
"EV board", "Kiosk", "OrchestraX", "WUIH",
|
||||
"WUIH_DEV", "CAT_DEV_WUIH",
|
||||
};
|
||||
|
||||
const char* const keysetLookup[] = {
|
||||
"Factory",
|
||||
"Debug",
|
||||
"Retail",
|
||||
"Invalid"
|
||||
};
|
||||
|
||||
const std::map<uint32_t, const char*> hardwareVersionMap = {
|
||||
{ 0x21100010, "LATTE_A11_EV" },
|
||||
{ 0x21100020, "LATTE_A11_CAT" },
|
||||
{ 0x21200010, "LATTE_A12_EV" },
|
||||
{ 0x21200020, "LATTE_A12_CAT" },
|
||||
{ 0x22100010, "LATTE_A2X_EV" },
|
||||
{ 0x22100020, "LATTE_A2X_CAT" },
|
||||
{ 0x23100010, "LATTE_A3X_EV" },
|
||||
{ 0x23100020, "LATTE_A3X_CAT" },
|
||||
{ 0x23100028, "LATTE_A3X_CAFE" },
|
||||
{ 0x24100010, "LATTE_A4X_EV" },
|
||||
{ 0x24100020, "LATTE_A4X_CAT" },
|
||||
{ 0x24100028, "LATTE_A4X_CAFE" },
|
||||
{ 0x25100010, "LATTE_A5X_EV" },
|
||||
{ 0x25100011, "LATTE_A5X_EV_Y" },
|
||||
{ 0x25100020, "LATTE_A5X_CAT" },
|
||||
{ 0x25100028, "LATTE_A5X_CAFE" },
|
||||
{ 0x26100010, "LATTE_B1X_EV" },
|
||||
{ 0x26100011, "LATTE_B1X_EV_Y" },
|
||||
{ 0x26100020, "LATTE_B1X_CAT" },
|
||||
{ 0x26100028, "LATTE_B1X_CAFE" },
|
||||
};
|
||||
|
||||
const char* const sataDeviceLookup[] = {
|
||||
"Invalid", "Default", "No device", "ROM drive",
|
||||
"R drive", "MION", "SES", "GEN2-HDD",
|
||||
"GEN1-HDD",
|
||||
};
|
||||
|
||||
const char* const regionLookup[] = {
|
||||
"JPN", "USA", "EUR", "AUS",
|
||||
"CHN", "KOR", "TWN",
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
GeneralScreen::GeneralScreen()
|
||||
{
|
||||
const auto& otp = OTP::Get();
|
||||
const auto& seeprom = SEEPROM::Get();
|
||||
|
||||
WUT_ALIGNAS(0x40) MCPSysProdSettings sysProd{};
|
||||
WUT_ALIGNAS(0x40) MCPSystemVersion sysVer{};
|
||||
int32_t mcpHandle = MCP_Open();
|
||||
if (mcpHandle >= 0) {
|
||||
// Don't bother checking res here, if it fails sysProd is zeroed
|
||||
MCP_GetSysProdSettings(mcpHandle, &sysProd);
|
||||
MCP_GetSystemVersion(mcpHandle, &sysVer);
|
||||
MCP_Close(mcpHandle);
|
||||
}
|
||||
|
||||
sysIdentList.push_back({"Model:", Utils::sprintf("%.*s", sizeof(sysProd.model_number), sysProd.model_number)});
|
||||
sysIdentList.push_back({"Serial:", Utils::sprintf("%.*s%.*s", sizeof(sysProd.code_id), sysProd.code_id, sizeof(sysProd.serial_id), sysProd.serial_id)});
|
||||
sysIdentList.push_back({"Production Date:",
|
||||
Utils::sprintf("%04x/%02x/%02x %02x:%02x",
|
||||
seeprom.prod_info.prod_year,
|
||||
seeprom.prod_info.prod_month_day >> 8,
|
||||
seeprom.prod_info.prod_month_day & 0xff,
|
||||
seeprom.prod_info.prod_hour_minute >> 8,
|
||||
seeprom.prod_info.prod_hour_minute & 0xff
|
||||
)
|
||||
});
|
||||
if (seeprom.bc.consoleType < 10) {
|
||||
hardwareList.push_back({"Type:", Utils::sprintf("%s (0x%02x)", consoleTypeLookup[seeprom.bc.consoleType], seeprom.bc.consoleType)});
|
||||
}
|
||||
sysIdentList.push_back({"Keyset:", keysetLookup[(otp.wiiUBank.securityLevel & 0x18000000) >> 27]});
|
||||
|
||||
BSPHardwareVersion hardwareVersion = 0;
|
||||
bspGetHardwareVersion(&hardwareVersion);
|
||||
const char* hardwareVersionString =
|
||||
hardwareVersionMap.contains(hardwareVersion) ? hardwareVersionMap.at(hardwareVersion)
|
||||
: "Unknown";
|
||||
hardwareList.push_back({"Version:", Utils::sprintf("%s", hardwareVersionString)});
|
||||
hardwareList.push_back({"", Utils::sprintf("(0x%08x)", hardwareVersion)});
|
||||
hardwareList.push_back({"Board Type:", Utils::sprintf("%c%c", seeprom.bc.boardType >> 8, seeprom.bc.boardType & 0xff)});
|
||||
if (seeprom.bc.sataDevice < 9) {
|
||||
hardwareList.push_back({"SATA Device:", Utils::sprintf("%s (0x%02x)", sataDeviceLookup[seeprom.bc.sataDevice], seeprom.bc.sataDevice)});
|
||||
}
|
||||
hardwareList.push_back({"DDR3 Size:", Utils::sprintf("%d MiB", seeprom.bc.ddr3Size)});
|
||||
hardwareList.push_back({"DDR3 Speed:", Utils::sprintf("%d", seeprom.bc.ddr3Speed)});
|
||||
hardwareList.push_back({"DDR3 Vendor:", Utils::sprintf("0x%02X", seeprom.bc.ddr3Vendor)});
|
||||
|
||||
const int productAreaId = sysProd.product_area ? __builtin_ctz(sysProd.product_area) : 7;
|
||||
if (productAreaId < 7) {
|
||||
regionList.push_back({"Product Area:", regionLookup[productAreaId]});
|
||||
}
|
||||
regionList.push_back({"Game Region:", ""});
|
||||
regionList.push_back({"",
|
||||
Utils::sprintf("%s %s %s %s %s %s (%u)",
|
||||
(sysProd.game_region & MCP_REGION_JAPAN) ? regionLookup[0] : "---",
|
||||
(sysProd.game_region & MCP_REGION_USA) ? regionLookup[1] : "---",
|
||||
(sysProd.game_region & MCP_REGION_EUROPE) ? regionLookup[2] : "---",
|
||||
(sysProd.game_region & MCP_REGION_CHINA) ? regionLookup[4] : "---",
|
||||
(sysProd.game_region & MCP_REGION_KOREA) ? regionLookup[5] : "---",
|
||||
(sysProd.game_region & MCP_REGION_TAIWAN) ? regionLookup[6] : "---", sysProd.game_region)
|
||||
});
|
||||
|
||||
versionList.push_back({"System Version:", Utils::sprintf("%u.%u.%u%c", sysVer.major, sysVer.minor, sysVer.patch, sysVer.region)});
|
||||
|
||||
// Need to decrypt boot1 info before using
|
||||
SEEPROM::Boot1Info boot1_info[2]{};
|
||||
if (Utils::AESDecrypt(otp.wiiUBank.seepromKey, 128, nullptr, &seeprom.boot1_info[0], &boot1_info[0], sizeof(SEEPROM::Boot1Info)) == 0 &&
|
||||
Utils::AESDecrypt(otp.wiiUBank.seepromKey, 128, nullptr, &seeprom.boot1_info[1], &boot1_info[1], sizeof(SEEPROM::Boot1Info)) == 0) {
|
||||
versionList.push_back({"Boot1 Version (0/1):", Utils::sprintf("0x%04x (%u)", boot1_info[0].version, boot1_info[0].version)});
|
||||
versionList.push_back({"Boot1 Version (1/1):", Utils::sprintf("0x%04x (%u)", boot1_info[1].version, boot1_info[1].version)});
|
||||
}
|
||||
}
|
||||
|
||||
GeneralScreen::~GeneralScreen()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void GeneralScreen::Draw()
|
||||
{
|
||||
DrawTopBar("General System Information");
|
||||
|
||||
int yOff = 128;
|
||||
yOff = DrawHeader(32, yOff, 896, 0xf02a, "Identification");
|
||||
yOff = DrawList(32, yOff, 896, sysIdentList);
|
||||
|
||||
yOff = DrawHeader(32, yOff, 896, 0xf538, "Hardware");
|
||||
yOff = DrawList(32, yOff, 896, hardwareList);
|
||||
|
||||
yOff = 128;
|
||||
yOff = DrawHeader(992, yOff, 896, 0xf0ac, "Region");
|
||||
yOff = DrawList(992, yOff, 896, regionList);
|
||||
|
||||
yOff = DrawHeader(992, yOff, 896, 0xf886, "Versions");
|
||||
yOff = DrawList(992, yOff, 896, versionList);
|
||||
|
||||
DrawBottomBar(nullptr, "\ue044 Exit", "\ue001 Back");
|
||||
}
|
||||
|
||||
bool GeneralScreen::Update(VPADStatus& input)
|
||||
{
|
||||
if (input.trigger & VPAD_BUTTON_B) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
20
source/screens/GeneralScreen.hpp
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "Screen.hpp"
|
||||
|
||||
class GeneralScreen : public Screen
|
||||
{
|
||||
public:
|
||||
GeneralScreen();
|
||||
virtual ~GeneralScreen();
|
||||
|
||||
void Draw();
|
||||
|
||||
bool Update(VPADStatus& input);
|
||||
|
||||
private:
|
||||
ScreenList sysIdentList;
|
||||
ScreenList hardwareList;
|
||||
ScreenList regionList;
|
||||
ScreenList versionList;
|
||||
};
|
136
source/screens/MainScreen.cpp
Normal file
@ -0,0 +1,136 @@
|
||||
#include "MainScreen.hpp"
|
||||
#include "MenuScreen.hpp"
|
||||
#include "Gfx.hpp"
|
||||
#include "system/OTP.hpp"
|
||||
#include "system/SEEPROM.hpp"
|
||||
#include "system/MemoryDevice.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <mocha/mocha.h>
|
||||
|
||||
MainScreen::~MainScreen()
|
||||
{
|
||||
if (state > STATE_INIT_MOCHA) {
|
||||
Mocha_DeInitLibrary();
|
||||
}
|
||||
}
|
||||
|
||||
void MainScreen::Draw()
|
||||
{
|
||||
Gfx::Clear(Gfx::COLOR_BACKGROUND);
|
||||
|
||||
if (menuScreen) {
|
||||
menuScreen->Draw();
|
||||
return;
|
||||
}
|
||||
|
||||
DrawTopBar(nullptr);
|
||||
|
||||
switch (state) {
|
||||
case STATE_INIT:
|
||||
break;
|
||||
case STATE_INIT_MOCHA:
|
||||
if (stateFailure) {
|
||||
DrawStatus("Failed to initialize mocha!\nMake sure to update or install Tiramisu/Aroma.", Gfx::COLOR_ERROR);
|
||||
break;
|
||||
}
|
||||
|
||||
DrawStatus("Initializing mocha...");
|
||||
break;
|
||||
case STATE_INIT_OTP:
|
||||
if (stateFailure) {
|
||||
DrawStatus("Failed to initialize OTP!", Gfx::COLOR_ERROR);
|
||||
break;
|
||||
}
|
||||
|
||||
DrawStatus("Initializing OTP...");
|
||||
break;
|
||||
case STATE_INIT_SEEPROM:
|
||||
if (stateFailure) {
|
||||
DrawStatus("Failed to initialize SEEPROM!", Gfx::COLOR_ERROR);
|
||||
break;
|
||||
}
|
||||
|
||||
DrawStatus("Initializing SEEPROM...");
|
||||
break;
|
||||
case STATE_INIT_MEMORY_DEVICE:
|
||||
if (stateFailure) {
|
||||
DrawStatus("Failed to initialize Memory Devices!", Gfx::COLOR_ERROR);
|
||||
break;
|
||||
}
|
||||
|
||||
DrawStatus("Initializing Memory Devices...");
|
||||
break;
|
||||
case STATE_LOAD_MENU:
|
||||
DrawStatus("Loading menu...");
|
||||
break;
|
||||
case STATE_IN_MENU:
|
||||
break;
|
||||
}
|
||||
|
||||
DrawBottomBar(stateFailure ? nullptr : "Please wait...", stateFailure ? "\ue044 Exit" : nullptr, nullptr);
|
||||
}
|
||||
|
||||
bool MainScreen::Update(VPADStatus& input)
|
||||
{
|
||||
if (menuScreen) {
|
||||
if (!menuScreen->Update(input)) {
|
||||
// menu wants to exit
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
MochaUtilsStatus status;
|
||||
switch (state) {
|
||||
case STATE_INIT:
|
||||
state = STATE_INIT_MOCHA;
|
||||
break;
|
||||
case STATE_INIT_MOCHA:
|
||||
status = Mocha_InitLibrary();
|
||||
if (status == MOCHA_RESULT_SUCCESS) {
|
||||
state = STATE_INIT_OTP;
|
||||
break;
|
||||
}
|
||||
|
||||
stateFailure = true;
|
||||
break;
|
||||
case STATE_INIT_OTP:
|
||||
if (OTP::Init()) {
|
||||
state = STATE_INIT_SEEPROM;
|
||||
break;
|
||||
}
|
||||
|
||||
stateFailure = true;
|
||||
break;
|
||||
case STATE_INIT_SEEPROM:
|
||||
if (SEEPROM::Init()) {
|
||||
state = STATE_INIT_MEMORY_DEVICE;
|
||||
break;
|
||||
}
|
||||
|
||||
stateFailure = true;
|
||||
break;
|
||||
case STATE_INIT_MEMORY_DEVICE:
|
||||
if (MemoryDevice::Init()) {
|
||||
state = STATE_LOAD_MENU;
|
||||
break;
|
||||
}
|
||||
|
||||
stateFailure = true;
|
||||
break;
|
||||
case STATE_LOAD_MENU:
|
||||
menuScreen = std::make_unique<MenuScreen>();
|
||||
break;
|
||||
case STATE_IN_MENU:
|
||||
break;
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MainScreen::DrawStatus(std::string status, SDL_Color color)
|
||||
{
|
||||
Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, color, status, Gfx::ALIGN_CENTER);
|
||||
}
|
33
source/screens/MainScreen.hpp
Normal file
@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include "Screen.hpp"
|
||||
#include "Gfx.hpp"
|
||||
#include <memory>
|
||||
|
||||
class MainScreen : public Screen
|
||||
{
|
||||
public:
|
||||
MainScreen() = default;
|
||||
virtual ~MainScreen();
|
||||
|
||||
void Draw();
|
||||
|
||||
bool Update(VPADStatus& input);
|
||||
|
||||
protected:
|
||||
void DrawStatus(std::string status, SDL_Color color = Gfx::COLOR_TEXT);
|
||||
|
||||
private:
|
||||
enum {
|
||||
STATE_INIT,
|
||||
STATE_INIT_MOCHA,
|
||||
STATE_INIT_OTP,
|
||||
STATE_INIT_SEEPROM,
|
||||
STATE_INIT_MEMORY_DEVICE,
|
||||
STATE_LOAD_MENU,
|
||||
STATE_IN_MENU,
|
||||
} state = STATE_INIT;
|
||||
bool stateFailure = false;
|
||||
|
||||
std::unique_ptr<Screen> menuScreen;
|
||||
};
|
89
source/screens/MenuScreen.cpp
Normal file
@ -0,0 +1,89 @@
|
||||
#include "MenuScreen.hpp"
|
||||
#include "Gfx.hpp"
|
||||
#include "AboutScreen.hpp"
|
||||
#include "GeneralScreen.hpp"
|
||||
#include "StorageScreen.hpp"
|
||||
#include "SubmitScreen.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
MenuScreen::MenuScreen()
|
||||
: entries({
|
||||
{ MENU_ID_GENERAL, { 0xf085, "General System Information" }},
|
||||
{ MENU_ID_STORAGE, { 0xf7c2, "Storage Information" }},
|
||||
{ MENU_ID_SUBMIT, { 0xf0ee, "Submit System Information" }},
|
||||
// { MENU_ID_TITLE, { 0xf022, "Title Information" }},
|
||||
{ MENU_ID_ABOUT, { 0xf05a, "About WiiUIdent" }},
|
||||
// { MENU_ID_EXIT, { 0xf057, "Exit" }},
|
||||
})
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
MenuScreen::~MenuScreen()
|
||||
{
|
||||
}
|
||||
|
||||
void MenuScreen::Draw()
|
||||
{
|
||||
if (subscreen) {
|
||||
subscreen->Draw();
|
||||
return;
|
||||
}
|
||||
|
||||
DrawTopBar(nullptr);
|
||||
|
||||
// draw entries
|
||||
for (MenuID id = MENU_ID_MIN; id <= MENU_ID_MAX; id = static_cast<MenuID>(id + 1)) {
|
||||
int yOff = 75 + static_cast<int>(id) * 150;
|
||||
Gfx::DrawRectFilled(0, yOff, Gfx::SCREEN_WIDTH, 150, Gfx::COLOR_ALT_BACKGROUND);
|
||||
Gfx::DrawIcon(68, yOff + 150 / 2, 60, Gfx::COLOR_TEXT, entries[id].icon);
|
||||
Gfx::Print(128 + 8, yOff + 150 / 2, 60, Gfx::COLOR_TEXT, entries[id].name, Gfx::ALIGN_VERTICAL);
|
||||
|
||||
if (id == selected) {
|
||||
Gfx::DrawRect(0, yOff, Gfx::SCREEN_WIDTH, 150, 8, Gfx::COLOR_HIGHLIGHTED);
|
||||
}
|
||||
}
|
||||
|
||||
DrawBottomBar("\ue07d Navigate", "\ue044 Exit", "\ue000 Select");
|
||||
}
|
||||
|
||||
bool MenuScreen::Update(VPADStatus& input)
|
||||
{
|
||||
if (subscreen) {
|
||||
if (!subscreen->Update(input)) {
|
||||
// subscreen wants to exit
|
||||
subscreen.reset();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (input.trigger & VPAD_BUTTON_DOWN) {
|
||||
if (selected < MENU_ID_MAX) {
|
||||
selected = static_cast<MenuID>(selected + 1);
|
||||
}
|
||||
} else if (input.trigger & VPAD_BUTTON_UP) {
|
||||
if (selected > MENU_ID_MIN) {
|
||||
selected = static_cast<MenuID>(selected - 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (input.trigger & VPAD_BUTTON_A) {
|
||||
switch (selected) {
|
||||
case MENU_ID_GENERAL:
|
||||
subscreen = std::make_unique<GeneralScreen>();
|
||||
break;
|
||||
case MENU_ID_STORAGE:
|
||||
subscreen = std::make_unique<StorageScreen>();
|
||||
break;
|
||||
case MENU_ID_SUBMIT:
|
||||
subscreen = std::make_unique<SubmitScreen>();
|
||||
break;
|
||||
case MENU_ID_ABOUT:
|
||||
subscreen = std::make_unique<AboutScreen>();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
36
source/screens/MenuScreen.hpp
Normal file
@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include "Screen.hpp"
|
||||
#include <memory>
|
||||
#include <map>
|
||||
|
||||
class MenuScreen : public Screen
|
||||
{
|
||||
public:
|
||||
MenuScreen();
|
||||
virtual ~MenuScreen();
|
||||
|
||||
void Draw();
|
||||
|
||||
bool Update(VPADStatus& input);
|
||||
|
||||
private:
|
||||
std::unique_ptr<Screen> subscreen;
|
||||
|
||||
enum MenuID {
|
||||
MENU_ID_GENERAL,
|
||||
MENU_ID_STORAGE,
|
||||
MENU_ID_SUBMIT,
|
||||
MENU_ID_ABOUT,
|
||||
|
||||
MENU_ID_MIN = MENU_ID_GENERAL,
|
||||
MENU_ID_MAX = MENU_ID_ABOUT,
|
||||
};
|
||||
|
||||
struct MenuEntry {
|
||||
uint16_t icon;
|
||||
const char* name;
|
||||
};
|
||||
std::map<MenuID, MenuEntry> entries;
|
||||
MenuID selected = MENU_ID_MIN;
|
||||
};
|
64
source/screens/StorageScreen.cpp
Normal file
@ -0,0 +1,64 @@
|
||||
#include "StorageScreen.hpp"
|
||||
#include "system/MemoryDevice.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include <functional>
|
||||
|
||||
StorageScreen::StorageScreen()
|
||||
{
|
||||
for (const MemoryDevice& dev : MemoryDevice::GetDevices()) {
|
||||
ScreenList* list = nullptr;
|
||||
if (dev.GetType() == MemoryDevice::TYPE_MLC) {
|
||||
list = &mlcList;
|
||||
} else if (dev.GetType() == MemoryDevice::TYPE_SD_CARD) {
|
||||
list = &sdList;
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
list->push_back({"Type:", dev.GetCardType() == MemoryDevice::CARD_TYPE_SD ? "SD" : "MMC"});
|
||||
list->push_back({"Manufacturer:", Utils::sprintf("%s (0x%02x)", dev.GetManufacturerName().c_str(), dev.GetMID())});
|
||||
list->push_back({"Product Name:", {dev.GetName(), true}});
|
||||
list->push_back({"Product Revision:", Utils::sprintf("%d.%d (0x%02x)", dev.GetPRV() >> 4, dev.GetPRV() & 0xf, dev.GetPRV())});
|
||||
if (dev.GetType() == MemoryDevice::TYPE_MLC)
|
||||
list->push_back({"Production Date:", dev.GetProductionDate()});
|
||||
list->push_back({"Size:", Utils::sprintf("%llu MiB", dev.GetTotalSize() / 1024ull / 1024ull)});
|
||||
list->push_back({"CID:", {Utils::ToHexString(dev.GetCID().data(), dev.GetCID().size()), true}});
|
||||
list->push_back({"CSD:", {Utils::ToHexString(dev.GetCSD().data(), dev.GetCSD().size()), true}});
|
||||
}
|
||||
|
||||
if (mlcList.empty()) {
|
||||
mlcList.push_back({"Not Attached", ""});
|
||||
}
|
||||
|
||||
if (sdList.empty()) {
|
||||
sdList.push_back({"Not Attached", ""});
|
||||
}
|
||||
}
|
||||
|
||||
StorageScreen::~StorageScreen()
|
||||
{
|
||||
}
|
||||
|
||||
void StorageScreen::Draw()
|
||||
{
|
||||
DrawTopBar("Storage Information");
|
||||
|
||||
int yOff = 128;
|
||||
yOff = DrawHeader(32, yOff, 896, 0xf2db, "MLC");
|
||||
yOff = DrawList(32, yOff, 896, mlcList);
|
||||
|
||||
yOff = 128;
|
||||
yOff = DrawHeader(992, yOff, 896, 0xf7c2, "SD Card");
|
||||
yOff = DrawList(992, yOff, 896, sdList);
|
||||
|
||||
DrawBottomBar(nullptr, "\ue044 Exit", "\ue001 Back");
|
||||
}
|
||||
|
||||
bool StorageScreen::Update(VPADStatus& input)
|
||||
{
|
||||
if (input.trigger & VPAD_BUTTON_B) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
18
source/screens/StorageScreen.hpp
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include "Screen.hpp"
|
||||
|
||||
class StorageScreen : public Screen
|
||||
{
|
||||
public:
|
||||
StorageScreen();
|
||||
virtual ~StorageScreen();
|
||||
|
||||
void Draw();
|
||||
|
||||
bool Update(VPADStatus& input);
|
||||
|
||||
private:
|
||||
ScreenList mlcList;
|
||||
ScreenList sdList;
|
||||
};
|
282
source/screens/SubmitScreen.cpp
Normal file
@ -0,0 +1,282 @@
|
||||
#include "SubmitScreen.hpp"
|
||||
#include "Gfx.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "system/OTP.hpp"
|
||||
#include "system/SEEPROM.hpp"
|
||||
#include "system/MemoryDevice.hpp"
|
||||
|
||||
#include <coreinit/bsp.h>
|
||||
#include <coreinit/mcp.h>
|
||||
|
||||
#include <curl/curl.h>
|
||||
#include <cacert_pem.h>
|
||||
|
||||
// POST data
|
||||
struct post_data {
|
||||
char system_model[16]; // [0x000] seeprom[0xB8]
|
||||
char system_serial[16]; // [0x010] seeprom[0xAC] + seeprom[0xB0] - need to mask the last 3 digits
|
||||
uint8_t mfg_date[6]; // [0x020] seeprom[0xC4]
|
||||
uint8_t productArea; // [0x026]
|
||||
uint8_t gameRegion; // [0x027]
|
||||
uint32_t sec_level; // [0x028] otp[0x080]
|
||||
uint16_t boardType; // [0x02C] seeprom[0x21]
|
||||
uint16_t boardRevision; // [0x02E] seeprom[0x22]
|
||||
uint16_t bootSource; // [0x030] seeprom[0x23]
|
||||
uint16_t ddr3Size; // [0x032] seeprom[0x24]
|
||||
uint16_t ddr3Speed; // [0x034] seeprom[0x25]
|
||||
uint16_t sataDevice; // [0x036] seeprom[0x2C]
|
||||
uint16_t consoleType; // [0x038] seeprom[0x2D]
|
||||
uint16_t reserved1; // [0x03A]
|
||||
uint32_t bsp_rev; // [0x03C] bspGetHardwareVersion();
|
||||
uint16_t ddr3Vendor; // [0x040] seeprom[0x29]
|
||||
uint8_t reserved2[62]; // [0x042]
|
||||
|
||||
// [0x080]
|
||||
struct {
|
||||
uint32_t cid[4]; // [0x080] CID
|
||||
uint32_t mid_prv; // [0x090] Manufacturer and product revision
|
||||
uint32_t blockSize; // [0x094] Block size
|
||||
uint64_t numBlocks; // [0x098] Number of blocks
|
||||
char name1[128]; // [0x0A0] Product name
|
||||
} mlc;
|
||||
|
||||
// [0x120]
|
||||
uint8_t otp_sha256[32]; // [0x120] OTP SHA-256 hash (to prevent duplicates)
|
||||
}; // size == 0x140 (320)
|
||||
WUT_CHECK_SIZE(post_data, 0x140);
|
||||
|
||||
struct post_data_hashed {
|
||||
struct post_data data;
|
||||
uint8_t post_sha256[32]; // [0x140] SHA-256 hash of post_data, with adjustments
|
||||
}; // size == 0x160 (352)
|
||||
WUT_CHECK_SIZE(post_data_hashed, 0x160);
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
const char* desc =
|
||||
"This will submit statistical data to the developers of WiiUIdent,\n"
|
||||
"which will help to determine various statistics about Wii U consoles,\n"
|
||||
"e.g. eMMC manufacturers. The submitted data may be publicly accessible\n"
|
||||
"but personally identifying information will be kept confidential.\n"
|
||||
"\n"
|
||||
"Information that will be submitted:\n"
|
||||
"\uff65 System model\n"
|
||||
"\uff65 System serial number (excluding the last 3 digits)\n"
|
||||
"\uff65 Manufacturing date\n"
|
||||
"\uff65 Region information\n"
|
||||
"\uff65 Security level (keyset), sataDevice, consoleType, BSP revision\n"
|
||||
"\uff65 boardType, boardRevision, bootSource, ddr3Size, ddr3Speed, ddr3Vendor\n"
|
||||
"\uff65 MLC manufacturer, revision, name, size, and CID\n"
|
||||
"\uff65 SHA-256 hash of OTP (to prevent duplicates)\n"
|
||||
"\n"
|
||||
"Do you want to submit your console's system data?\n";
|
||||
}
|
||||
|
||||
SubmitScreen::SubmitScreen()
|
||||
{
|
||||
}
|
||||
|
||||
SubmitScreen::~SubmitScreen()
|
||||
{
|
||||
}
|
||||
|
||||
void SubmitScreen::Draw()
|
||||
{
|
||||
DrawTopBar("Submit System Information");
|
||||
|
||||
if (state == STATE_INFO) {
|
||||
Gfx::Print(32, 75 + 32, 40, Gfx::COLOR_TEXT, desc);
|
||||
|
||||
DrawBottomBar(nullptr, "\ue044 Exit", "\ue001 Back / \ue000 Submit");
|
||||
} else if (state == STATE_SUBMITTING) {
|
||||
Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_TEXT, "Submitting info...", Gfx::ALIGN_CENTER);
|
||||
|
||||
DrawBottomBar("Please wait...", nullptr, nullptr);
|
||||
} else if (state == STATE_SUBMITTED) {
|
||||
if (!error.empty() && response.empty()) {
|
||||
Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 40, Gfx::COLOR_ERROR, Utils::sprintf("Error!\n%s", error.c_str()), Gfx::ALIGN_CENTER);
|
||||
|
||||
Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT - 75 - 32, 40, Gfx::COLOR_TEXT,
|
||||
"Failed to submit system data. Please report a bug on GitHub:\n"
|
||||
"https://github.com/GaryOderNichts/WiiUIdent/issues", Gfx::ALIGN_HORIZONTAL | Gfx::ALIGN_BOTTOM);
|
||||
} else if (!response.empty()) {
|
||||
Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 40, Gfx::COLOR_TEXT, response, Gfx::ALIGN_CENTER);
|
||||
|
||||
if (!error.empty()) {
|
||||
Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT - 75 - 32, 40, Gfx::COLOR_TEXT,
|
||||
"Failed to submit system data. Please report a bug on GitHub:\n"
|
||||
"https://github.com/GaryOderNichts/WiiUIdent/issues", Gfx::ALIGN_HORIZONTAL | Gfx::ALIGN_BOTTOM);
|
||||
} else {
|
||||
Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT - 75 - 32, 40, Gfx::COLOR_TEXT,
|
||||
"System data submitted successfully. Check out the Wii U console database at:\n"
|
||||
"https://" DATABASE_URL "/", Gfx::ALIGN_HORIZONTAL | Gfx::ALIGN_BOTTOM);
|
||||
}
|
||||
} else {
|
||||
Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_TEXT, "No response.", Gfx::ALIGN_CENTER);
|
||||
}
|
||||
|
||||
DrawBottomBar(nullptr, "\ue044 Exit", "\ue001 Back");
|
||||
}
|
||||
}
|
||||
|
||||
bool SubmitScreen::Update(VPADStatus& input)
|
||||
{
|
||||
if (state == STATE_INFO) {
|
||||
if (input.trigger & VPAD_BUTTON_A) {
|
||||
state = STATE_SUBMITTING;
|
||||
} else if (input.trigger & VPAD_BUTTON_B) {
|
||||
return false;
|
||||
}
|
||||
} else if (state == STATE_SUBMITTING) {
|
||||
SubmitSystemData();
|
||||
state = STATE_SUBMITTED;
|
||||
} else if (state == STATE_SUBMITTED) {
|
||||
if (input.trigger & VPAD_BUTTON_B) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t SubmitScreen::CurlWriteCallback(void* contents, size_t size, size_t nmemb, void* userp)
|
||||
{
|
||||
size_t realsize = size * nmemb;
|
||||
SubmitScreen* screen = (SubmitScreen*)userp;
|
||||
|
||||
if (screen->response.size() < 4096) { // let's add a limit here to be safe
|
||||
screen->response += std::string((char*) contents, (char*) contents + realsize);
|
||||
}
|
||||
|
||||
return realsize;
|
||||
}
|
||||
|
||||
void SubmitScreen::SubmitSystemData()
|
||||
{
|
||||
const auto& otp = OTP::Get();
|
||||
const auto& seeprom = SEEPROM::Get();
|
||||
WUT_ALIGNAS(0x40) MCPSysProdSettings sysProd{};
|
||||
int32_t mcpHandle = MCP_Open();
|
||||
if (mcpHandle >= 0) {
|
||||
// Don't bother checking res here, if it fails sysProd is zeroed
|
||||
MCP_GetSysProdSettings(mcpHandle, &sysProd);
|
||||
MCP_Close(mcpHandle);
|
||||
}
|
||||
const MemoryDevice* mlcDev = nullptr;
|
||||
for (const MemoryDevice& dev : MemoryDevice::GetDevices()) {
|
||||
if (dev.GetType() == MemoryDevice::TYPE_MLC) {
|
||||
mlcDev = &dev;
|
||||
}
|
||||
}
|
||||
|
||||
post_data_hashed pdh{};
|
||||
post_data* pd = &pdh.data;
|
||||
memcpy(pd->system_model, seeprom.sys_prod.model_numer, sizeof(pd->system_model));
|
||||
memcpy(pd->mfg_date, &seeprom.prod_info.prod_year, sizeof(pd->mfg_date));
|
||||
pd->sec_level = otp.wiiUBank.securityLevel;
|
||||
pd->boardType = seeprom.bc.boardType;
|
||||
pd->boardRevision = seeprom.bc.boardRevision;
|
||||
pd->bootSource = seeprom.bc.bootSource;
|
||||
pd->ddr3Size = seeprom.bc.ddr3Size;
|
||||
pd->ddr3Speed = seeprom.bc.ddr3Speed;
|
||||
pd->ddr3Vendor = seeprom.bc.ddr3Vendor;
|
||||
pd->sataDevice = seeprom.bc.sataDevice;
|
||||
pd->consoleType = seeprom.bc.consoleType;
|
||||
|
||||
if (bspGetHardwareVersion(&pd->bsp_rev) != 0) {
|
||||
pd->bsp_rev = 0;
|
||||
}
|
||||
|
||||
// System serial number
|
||||
// NOTE: Assuming code+serial doesn't exceed 15 chars (plus NULL).
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wformat-truncation"
|
||||
snprintf(pd->system_serial, sizeof(pd->system_serial), "%s%s",
|
||||
seeprom.sys_prod.code_id, seeprom.sys_prod.serial_id);
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
// Mask the last 3 digits of the system serial number.
|
||||
for (unsigned int i = sizeof(pd->system_serial)-1; i > 3; i--) {
|
||||
if (pd->system_serial[i] <= 0x20) {
|
||||
pd->system_serial[i] = 0;
|
||||
continue;
|
||||
}
|
||||
// Found printable text.
|
||||
// Mask the last three digits.
|
||||
pd->system_serial[i-0] = '*';
|
||||
pd->system_serial[i-1] = '*';
|
||||
pd->system_serial[i-2] = '*';
|
||||
break;
|
||||
}
|
||||
|
||||
if (sysProd.product_area) {
|
||||
pd->productArea = __builtin_ctz(sysProd.product_area);
|
||||
}
|
||||
pd->gameRegion = sysProd.game_region;
|
||||
|
||||
if (mlcDev) {
|
||||
memcpy(pd->mlc.cid, mlcDev->GetCID().data(), sizeof(pd->mlc.cid));
|
||||
pd->mlc.mid_prv = mlcDev->GetMID() << 16 | mlcDev->GetPRV();
|
||||
pd->mlc.numBlocks = mlcDev->GetNumBlocks();
|
||||
pd->mlc.blockSize = mlcDev->GetBlockSize();
|
||||
strncpy(pd->mlc.name1, mlcDev->GetName().c_str(), sizeof(pd->mlc.name1) - 1);
|
||||
}
|
||||
|
||||
if (Utils::SHA256(&otp, sizeof(otp), pd->otp_sha256, sizeof(pd->otp_sha256)) != 0) {
|
||||
error = "Failed to hash otp data";
|
||||
return;
|
||||
}
|
||||
|
||||
char hashbuf[sizeof(*pd) + 64];
|
||||
memcpy(hashbuf, pd, sizeof(*pd));
|
||||
memcpy(hashbuf + sizeof(*pd), "This will submit statistical data to the developers of recovery_menu", 64);
|
||||
if (Utils::SHA256(hashbuf, sizeof(hashbuf), pdh.post_sha256, sizeof(pdh.post_sha256)) != 0) {
|
||||
error = "Failed to hash data";
|
||||
return;
|
||||
}
|
||||
|
||||
CURL* curl = curl_easy_init();
|
||||
if (!curl) {
|
||||
error = "Failed to init curl!";
|
||||
return;
|
||||
}
|
||||
|
||||
// setup post
|
||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, "WiiUIdent/" APP_VERSION);
|
||||
curl_easy_setopt(curl, CURLOPT_URL, "https://" DATABASE_URL "/add-system.php");
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, sizeof(pdh));
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, &pdh);
|
||||
|
||||
// set content type to octet stream
|
||||
struct curl_slist* list = nullptr;
|
||||
list = curl_slist_append(list, "Content-Type: application/octet-stream");
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
|
||||
|
||||
// set write callback for response
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteCallback);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, this);
|
||||
|
||||
// load certs
|
||||
struct curl_blob blob{};
|
||||
blob.data = (void *) cacert_pem;
|
||||
blob.len = cacert_pem_size;
|
||||
blob.flags = CURL_BLOB_COPY;
|
||||
curl_easy_setopt(curl, CURLOPT_CAINFO_BLOB, &blob);
|
||||
|
||||
CURLcode res = curl_easy_perform(curl);
|
||||
if (res != CURLE_OK) {
|
||||
error = Utils::sprintf("Failed to upload data:\n%s (%d)", curl_easy_strerror(res), res);
|
||||
curl_easy_cleanup(curl);
|
||||
return;
|
||||
}
|
||||
|
||||
long code;
|
||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
|
||||
if (code >= 400) {
|
||||
error = Utils::sprintf("Server responded with %d!", code);
|
||||
}
|
||||
|
||||
curl_easy_cleanup(curl);
|
||||
return;
|
||||
}
|
29
source/screens/SubmitScreen.hpp
Normal file
@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "Screen.hpp"
|
||||
|
||||
class SubmitScreen : public Screen
|
||||
{
|
||||
public:
|
||||
SubmitScreen();
|
||||
virtual ~SubmitScreen();
|
||||
|
||||
void Draw();
|
||||
|
||||
bool Update(VPADStatus& input);
|
||||
|
||||
private:
|
||||
enum State {
|
||||
STATE_INFO,
|
||||
STATE_SUBMITTING,
|
||||
STATE_SUBMITTED,
|
||||
};
|
||||
State state = STATE_INFO;
|
||||
|
||||
static size_t CurlWriteCallback(void* contents, size_t size, size_t nmemb, void* userp);
|
||||
|
||||
void SubmitSystemData();
|
||||
|
||||
std::string error;
|
||||
std::string response;
|
||||
};
|
178
source/system/MemoryDevice.cpp
Normal file
@ -0,0 +1,178 @@
|
||||
#include "MemoryDevice.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
|
||||
#include <mocha/mocha.h>
|
||||
|
||||
struct WUT_PACKED SALDeviceParams {
|
||||
uint32_t usrptr;
|
||||
uint32_t mid_prv;
|
||||
uint32_t device_type;
|
||||
WUT_UNKNOWN_BYTES(0x1c);
|
||||
uint64_t numBlocks;
|
||||
uint32_t blockSize;
|
||||
WUT_UNKNOWN_BYTES(0x18);
|
||||
char name0[128];
|
||||
char name1[128];
|
||||
char name2[128];
|
||||
uint32_t functions[12];
|
||||
};
|
||||
|
||||
struct WUT_PACKED MDBlkDrv {
|
||||
int32_t registered;
|
||||
WUT_UNKNOWN_BYTES(0x8);
|
||||
SALDeviceParams params;
|
||||
int sal_handle;
|
||||
int deviceId;
|
||||
WUT_UNKNOWN_BYTES(0xc4);
|
||||
};
|
||||
WUT_CHECK_SIZE(MDBlkDrv, 0x2d4);
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
constexpr uint32_t MDBLK_DRIVER_ADDRESS = 0x11c39e78;
|
||||
constexpr uint32_t MD_DEVICE_POINTERS_ADDRESS = 0x10899308;
|
||||
|
||||
// TODO turn this into a struct eventually
|
||||
constexpr uint32_t MD_DEVICE_CID_OFFSET = 0x58;
|
||||
constexpr uint32_t MD_DEVICE_CSD_OFFSET = 0x68;
|
||||
|
||||
const std::map<uint16_t, std::string> sdMidMap = {
|
||||
// from https://kernel.googlesource.com/pub/scm/utils/mmc/mmc-utils/+/refs/heads/master/lsmmc.c
|
||||
{ 0x01, "Panasonic" },
|
||||
{ 0x02, "Toshiba/Kingston/Viking" },
|
||||
{ 0x03, "SanDisk" },
|
||||
{ 0x08, "Silicon Power" },
|
||||
{ 0x18, "Infineon" },
|
||||
{ 0x1b, "Transcend/Samsung" },
|
||||
{ 0x1c, "Transcend" },
|
||||
{ 0x1d, "Corsair/AData" },
|
||||
{ 0x1e, "Transcend" },
|
||||
{ 0x1f, "Kingston" },
|
||||
{ 0x27, "Delkin/Phison" },
|
||||
{ 0x28, "Lexar" },
|
||||
{ 0x30, "SanDisk" },
|
||||
{ 0x31, "Silicon Power" },
|
||||
{ 0x33, "STMicroelectronics" },
|
||||
{ 0x41, "Kingston" },
|
||||
{ 0x6f, "STMicroelectronics" },
|
||||
{ 0x74, "Transcend" },
|
||||
{ 0x76, "Patriot" },
|
||||
{ 0x82, "Gobe/Sony" },
|
||||
{ 0x89, "Unknown" },
|
||||
{ 0x9e, "PNY" },
|
||||
};
|
||||
|
||||
const std::map<uint16_t, std::string> mmcMidMap = {
|
||||
{ 0x11, "Toshiba" },
|
||||
// technically shared with Samsung/SanDisk/LG but Nintendo only used Samsung in Wii Us
|
||||
{ 0x15, "Samsung" },
|
||||
{ 0x90, "Hynix" }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
bool MemoryDevice::Init()
|
||||
{
|
||||
memoryDevices.clear();
|
||||
|
||||
// read driver structs from IOS-FS
|
||||
MDBlkDrv blkDrvs[2]{};
|
||||
for (uint32_t i = 0; i < sizeof(blkDrvs) / 4; i++) {
|
||||
if (Mocha_IOSUKernelRead32(MDBLK_DRIVER_ADDRESS + (i * 4), ((uint32_t*) &blkDrvs) + i) != MOCHA_RESULT_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// read all SAL device pointers
|
||||
uint32_t devicePointers[8]{};
|
||||
for (uint32_t i = 0; i < sizeof(devicePointers) / 4; i++) {
|
||||
if (Mocha_IOSUKernelRead32(MD_DEVICE_POINTERS_ADDRESS + (i * 4), devicePointers + i) != MOCHA_RESULT_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a MemoryDevice instance for each struct
|
||||
for (MDBlkDrv& drv : blkDrvs) {
|
||||
// don't bother adding it if not attached
|
||||
if (!drv.registered) {
|
||||
continue;
|
||||
}
|
||||
|
||||
MemoryDevice dev{};
|
||||
dev.type = (Type) drv.params.device_type;
|
||||
dev.mid = drv.params.mid_prv >> 16;
|
||||
dev.prv = drv.params.mid_prv & 0xffff;
|
||||
dev.name = drv.params.name1;
|
||||
dev.numBlocks = drv.params.numBlocks;
|
||||
dev.blockSize = drv.params.blockSize;
|
||||
|
||||
// some manufacturers place weird characters into their names?
|
||||
dev.name.erase(std::remove_if(dev.name.begin(), dev.name.end(), [](char c){return !(c >= 0 && c < 128);}), dev.name.end());
|
||||
|
||||
// read cid and csd
|
||||
int idx = drv.deviceId - 0x42;
|
||||
if (idx >= 0 && idx < 8 && devicePointers[idx]) {
|
||||
for (uint32_t i = 0; i < dev.cid.size() / 4; i++) {
|
||||
if (Mocha_IOSUKernelRead32(devicePointers[idx] + MD_DEVICE_CID_OFFSET + (i * 4), (uint32_t*) dev.cid.data() + i) != MOCHA_RESULT_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < dev.csd.size() / 4; i++) {
|
||||
if (Mocha_IOSUKernelRead32(devicePointers[idx] + MD_DEVICE_CSD_OFFSET + (i * 4), (uint32_t*) dev.csd.data() + i) != MOCHA_RESULT_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
memoryDevices.push_back(dev);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
MemoryDevice::CardType MemoryDevice::GetCardType() const
|
||||
{
|
||||
// if we have a mmc manufacturer for this mid it's most likely a mmc
|
||||
if (mmcMidMap.contains(GetMID())) {
|
||||
return CARD_TYPE_MMC;
|
||||
}
|
||||
|
||||
// otherwise if we have a sd manufacturer it's probably an sd card
|
||||
if (sdMidMap.contains(GetMID())) {
|
||||
return CARD_TYPE_SD;
|
||||
}
|
||||
|
||||
return CARD_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
std::string MemoryDevice::GetManufacturerName() const
|
||||
{
|
||||
CardType cardType = GetCardType();
|
||||
if (cardType == CARD_TYPE_MMC) {
|
||||
return mmcMidMap.at(GetMID());
|
||||
} else if (cardType == CARD_TYPE_SD) {
|
||||
return sdMidMap.at(GetMID());
|
||||
}
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
std::string MemoryDevice::GetProductionDate() const
|
||||
{
|
||||
if (GetCardType() != CARD_TYPE_MMC) {
|
||||
// we currently don't read the production data for non mmc types
|
||||
return "";
|
||||
}
|
||||
|
||||
uint8_t month = (cid[14] >> 4) & 0xf;
|
||||
uint16_t year = cid[14] & 0xf;
|
||||
year += 1997;
|
||||
if(year < 2005)
|
||||
year += 0x10;
|
||||
|
||||
return Utils::sprintf("%u/%02u", year, month);
|
||||
}
|
61
source/system/MemoryDevice.hpp
Normal file
@ -0,0 +1,61 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <string>
|
||||
|
||||
class MemoryDevice
|
||||
{
|
||||
public:
|
||||
virtual ~MemoryDevice() = default;
|
||||
MemoryDevice(const MemoryDevice&) = default;
|
||||
|
||||
static bool Init();
|
||||
static const std::vector<MemoryDevice>& GetDevices() { return memoryDevices; }
|
||||
|
||||
public:
|
||||
enum Type {
|
||||
TYPE_MLC = 0x05,
|
||||
TYPE_SD_CARD = 0x06,
|
||||
// used instead of MLC if bsp_variant == 0x29 || bsp_variant == 0x21
|
||||
TYPE_UNKNOWN = 0x12,
|
||||
};
|
||||
|
||||
Type GetType() const { return type; }
|
||||
uint16_t GetMID() const { return mid; }
|
||||
uint16_t GetPRV() const { return prv; }
|
||||
std::string GetName() const { return name; }
|
||||
|
||||
uint64_t GetNumBlocks() const { return numBlocks; }
|
||||
uint32_t GetBlockSize() const { return blockSize; }
|
||||
uint64_t GetTotalSize() const { return GetNumBlocks() * GetBlockSize(); }
|
||||
|
||||
const std::array<uint8_t, 0x10>& GetCID() const { return cid; }
|
||||
const std::array<uint8_t, 0x10>& GetCSD() const { return csd; }
|
||||
|
||||
enum CardType {
|
||||
CARD_TYPE_MMC,
|
||||
CARD_TYPE_SD,
|
||||
CARD_TYPE_UNKNOWN,
|
||||
};
|
||||
|
||||
CardType GetCardType() const;
|
||||
std::string GetManufacturerName() const;
|
||||
std::string GetProductionDate() const;
|
||||
|
||||
private:
|
||||
MemoryDevice() = default;
|
||||
|
||||
static inline std::vector<MemoryDevice> memoryDevices = {};
|
||||
|
||||
private:
|
||||
Type type;
|
||||
uint16_t mid;
|
||||
uint16_t prv;
|
||||
std::string name;
|
||||
uint64_t numBlocks;
|
||||
uint32_t blockSize;
|
||||
std::array<uint8_t, 0x10> cid;
|
||||
std::array<uint8_t, 0x10> csd;
|
||||
};
|
12
source/system/OTP.cpp
Normal file
@ -0,0 +1,12 @@
|
||||
#include "OTP.hpp"
|
||||
|
||||
#include <mocha/mocha.h>
|
||||
|
||||
bool OTP::Init()
|
||||
{
|
||||
if (Mocha_ReadOTP(&cachedOTP) != MOCHA_RESULT_SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
16
source/system/OTP.hpp
Normal file
@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <mocha/otp.h>
|
||||
|
||||
class OTP
|
||||
{
|
||||
public:
|
||||
static bool Init();
|
||||
static const WiiUConsoleOTP& Get() { return cachedOTP; }
|
||||
|
||||
private:
|
||||
OTP() = default;
|
||||
virtual ~OTP() = default;
|
||||
|
||||
static inline WiiUConsoleOTP cachedOTP;
|
||||
};
|
12
source/system/SEEPROM.cpp
Normal file
@ -0,0 +1,12 @@
|
||||
#include "SEEPROM.hpp"
|
||||
|
||||
#include <mocha/mocha.h>
|
||||
|
||||
bool SEEPROM::Init()
|
||||
{
|
||||
if (Mocha_SEEPROMRead((uint8_t*) &cachedSEEPROMData, 0, sizeof(Data)) != sizeof(Data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
107
source/system/SEEPROM.hpp
Normal file
@ -0,0 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
#include <mocha/otp.h>
|
||||
|
||||
class SEEPROM
|
||||
{
|
||||
public:
|
||||
struct WUT_PACKED BoardConfig {
|
||||
uint32_t crc32;
|
||||
uint16_t size;
|
||||
uint16_t version;
|
||||
uint16_t author;
|
||||
uint16_t boardType;
|
||||
uint16_t boardRevision;
|
||||
uint16_t bootSource;
|
||||
uint16_t ddr3Size;
|
||||
uint16_t ddr3Speed;
|
||||
uint16_t ppcClockMultiplier;
|
||||
uint16_t iopClockMultiplier;
|
||||
uint16_t video1080p;
|
||||
uint16_t ddr3Vendor;
|
||||
uint16_t movPassiveReset;
|
||||
uint16_t sysPllSpeed;
|
||||
uint16_t sataDevice;
|
||||
uint16_t consoleType;
|
||||
uint32_t devicePresence;
|
||||
WUT_UNKNOWN_BYTES(0x20);
|
||||
};
|
||||
WUT_CHECK_SIZE(BoardConfig, 0x48);
|
||||
|
||||
struct WUT_PACKED SysProd {
|
||||
uint32_t version;
|
||||
uint32_t eeprom_version;
|
||||
uint32_t product_area;
|
||||
uint32_t game_region;
|
||||
uint32_t ntsc_pal;
|
||||
uint16_t wifi_5ghz_country_code;
|
||||
uint16_t wifi_5ghz_country_code_revision;
|
||||
char code_id[0x08];
|
||||
char serial_id[0x10];
|
||||
char model_numer[0x10];
|
||||
};
|
||||
WUT_CHECK_SIZE(SysProd, 0x40);
|
||||
|
||||
struct WUT_PACKED ProdInfo {
|
||||
WUT_UNKNOWN_BYTES(0x8);
|
||||
uint16_t prod_year;
|
||||
uint16_t prod_month_day;
|
||||
uint16_t prod_hour_minute;
|
||||
uint32_t crc32;
|
||||
};
|
||||
WUT_CHECK_SIZE(ProdInfo, 0x12);
|
||||
|
||||
struct WUT_PACKED BootParams {
|
||||
uint16_t cpu_flags;
|
||||
uint16_t nand_sd_flags;
|
||||
uint32_t nand_config;
|
||||
uint32_t nand_bank;
|
||||
uint32_t crc32;
|
||||
};
|
||||
WUT_CHECK_SIZE(BootParams, 0x10);
|
||||
|
||||
struct WUT_PACKED Boot1Info {
|
||||
uint16_t version;
|
||||
uint16_t sector;
|
||||
WUT_UNKNOWN_BYTES(0x8);
|
||||
uint32_t crc32;
|
||||
};
|
||||
WUT_CHECK_SIZE(Boot1Info, 0x10);
|
||||
|
||||
struct WUT_PACKED Data {
|
||||
WUT_UNKNOWN_BYTES(0x12);
|
||||
uint8_t rng_seed[0x08];
|
||||
WUT_UNKNOWN_BYTES(0x6);
|
||||
uint32_t ppc_pvr;
|
||||
char seeprom_version_name[0x06];
|
||||
uint16_t seeprom_version_code;
|
||||
uint16_t otp_version_code;
|
||||
uint16_t otp_revision_code;
|
||||
char otp_version_name[0x08];
|
||||
BoardConfig bc;
|
||||
uint8_t drive_key[0x10];
|
||||
uint8_t factory_key[0x10];
|
||||
uint8_t shdd_key[0x10];
|
||||
uint8_t usb_key_seed[0x10];
|
||||
uint16_t drive_key_status;
|
||||
uint16_t usk_key_status;
|
||||
uint16_t shdd_key_status;
|
||||
WUT_UNKNOWN_BYTES(0x7A);
|
||||
SysProd sys_prod;
|
||||
ProdInfo prod_info;
|
||||
uint16_t marker0;
|
||||
WUT_UNKNOWN_BYTES(0xe);
|
||||
uint16_t marker1;
|
||||
WUT_UNKNOWN_BYTES(0x1c);
|
||||
BootParams boot_params;
|
||||
Boot1Info boot1_info[2];
|
||||
WUT_UNKNOWN_BYTES(0x10);
|
||||
};
|
||||
WUT_CHECK_SIZE(Data, 0x200);
|
||||
|
||||
static bool Init();
|
||||
static const SEEPROM::Data& Get() { return cachedSEEPROMData; }
|
||||
|
||||
private:
|
||||
static inline SEEPROM::Data cachedSEEPROMData;
|
||||
};
|