mirror of
https://github.com/GaryOderNichts/DRXUtil.git
synced 2025-02-23 00:47:09 +01:00
Initial commit
This commit is contained in:
commit
787a4df381
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
/.vscode
|
||||
/build
|
||||
*.elf
|
||||
*.rpx
|
||||
*.wuhb
|
339
LICENSE
Normal file
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.
|
194
Makefile
Normal file
194
Makefile
Normal file
@ -0,0 +1,194 @@
|
||||
#-------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
ifeq ($(strip $(DEVKITPRO)),)
|
||||
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
|
||||
endif
|
||||
|
||||
TOPDIR ?= $(CURDIR)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# APP_NAME sets the long name of the application
|
||||
# APP_SHORTNAME sets the short name of the application
|
||||
# APP_AUTHOR sets the author of the application
|
||||
#-------------------------------------------------------------------------------
|
||||
APP_NAME := DRXUtil
|
||||
APP_SHORTNAME := DRXUtil
|
||||
APP_AUTHOR := GaryOderNichts
|
||||
APP_VERSION := 1.0
|
||||
|
||||
include $(DEVKITPRO)/wut/share/wut_rules
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# TARGET is the name of the output
|
||||
# BUILD is the directory where object files & intermediate files will be placed
|
||||
# SOURCES is a list of directories containing source code
|
||||
# DATA is a list of directories containing data files
|
||||
# INCLUDES is a list of directories containing header files
|
||||
# CONTENT is the path to the bundled folder that will be mounted as /vol/content/
|
||||
# ICON is the game icon, leave blank to use default rule
|
||||
# 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))
|
||||
BUILD := build
|
||||
SOURCES := source source/screens
|
||||
DATA := data
|
||||
INCLUDES := source include
|
||||
CONTENT :=
|
||||
ICON :=
|
||||
TV_SPLASH :=
|
||||
DRC_SPLASH :=
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#-------------------------------------------------------------------------------
|
||||
CFLAGS := -Wall -O2 -ffunction-sections \
|
||||
$(MACHDEP)
|
||||
|
||||
CFLAGS += $(INCLUDE) -D__WIIU__ -D__WUT__ -DAPP_VERSION=\"$(APP_VERSION)\"
|
||||
|
||||
CXXFLAGS := $(CFLAGS) -std=gnu++20
|
||||
|
||||
ASFLAGS := $(ARCH)
|
||||
LDFLAGS = $(ARCH) $(RPXSPECS) -Wl,-Map,$(notdir $*.map)
|
||||
|
||||
LIBS := -lSDL2 -lSDL2_ttf -lfreetype -lharfbuzz -lfreetype -lpng -lbz2 -lz -lmocha -lwut
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level
|
||||
# containing include and lib
|
||||
#-------------------------------------------------------------------------------
|
||||
LIBDIRS := $(PORTLIBS) $(WUT_ROOT) $(WUT_ROOT)/usr
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# no real need to edit anything past this point unless you need to add additional
|
||||
# rules for different file extensions
|
||||
#-------------------------------------------------------------------------------
|
||||
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||
export TOPDIR := $(CURDIR)
|
||||
|
||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
||||
|
||||
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||
|
||||
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# use CXX for linking C++ projects, CC for standard C
|
||||
#-------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(CPPFILES)),)
|
||||
#-------------------------------------------------------------------------------
|
||||
export LD := $(CC)
|
||||
#-------------------------------------------------------------------------------
|
||||
else
|
||||
#-------------------------------------------------------------------------------
|
||||
export LD := $(CXX)
|
||||
#-------------------------------------------------------------------------------
|
||||
endif
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
|
||||
export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||
export OFILES := $(OFILES_BIN) $(OFILES_SRC)
|
||||
export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES)))
|
||||
|
||||
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
||||
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
||||
-I$(CURDIR)/$(BUILD) -I$(DEVKITPRO)/portlibs/wiiu/include/SDL2
|
||||
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
|
||||
ifneq (,$(strip $(CONTENT)))
|
||||
export APP_CONTENT := $(TOPDIR)/$(CONTENT)
|
||||
endif
|
||||
|
||||
ifneq (,$(strip $(ICON)))
|
||||
export APP_ICON := $(TOPDIR)/$(ICON)
|
||||
else ifneq (,$(wildcard $(TOPDIR)/$(TARGET).png))
|
||||
export APP_ICON := $(TOPDIR)/$(TARGET).png
|
||||
else ifneq (,$(wildcard $(TOPDIR)/icon.png))
|
||||
export APP_ICON := $(TOPDIR)/icon.png
|
||||
endif
|
||||
|
||||
ifneq (,$(strip $(TV_SPLASH)))
|
||||
export APP_TV_SPLASH := $(TOPDIR)/$(TV_SPLASH)
|
||||
else ifneq (,$(wildcard $(TOPDIR)/tv-splash.png))
|
||||
export APP_TV_SPLASH := $(TOPDIR)/tv-splash.png
|
||||
else ifneq (,$(wildcard $(TOPDIR)/splash.png))
|
||||
export APP_TV_SPLASH := $(TOPDIR)/splash.png
|
||||
endif
|
||||
|
||||
ifneq (,$(strip $(DRC_SPLASH)))
|
||||
export APP_DRC_SPLASH := $(TOPDIR)/$(DRC_SPLASH)
|
||||
else ifneq (,$(wildcard $(TOPDIR)/drc-splash.png))
|
||||
export APP_DRC_SPLASH := $(TOPDIR)/drc-splash.png
|
||||
else ifneq (,$(wildcard $(TOPDIR)/splash.png))
|
||||
export APP_DRC_SPLASH := $(TOPDIR)/splash.png
|
||||
endif
|
||||
|
||||
.PHONY: $(BUILD) clean all
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
all: $(BUILD)
|
||||
|
||||
$(BUILD):
|
||||
@[ -d $@ ] || mkdir -p $@
|
||||
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
clean:
|
||||
@echo clean ...
|
||||
@rm -fr $(BUILD) $(TARGET).wuhb $(TARGET).rpx $(TARGET).elf
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
else
|
||||
.PHONY: all
|
||||
|
||||
DEPENDS := $(OFILES:.o=.d)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#-------------------------------------------------------------------------------
|
||||
all : $(OUTPUT).wuhb
|
||||
|
||||
$(OUTPUT).wuhb : $(OUTPUT).rpx
|
||||
$(OUTPUT).rpx : $(OUTPUT).elf
|
||||
$(OUTPUT).elf : $(OFILES)
|
||||
|
||||
$(OFILES_SRC) : $(HFILES_BIN)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# you need a rule like this for each extension you use as binary data
|
||||
#-------------------------------------------------------------------------------
|
||||
%.bin.o %_bin.h : %.bin
|
||||
#-------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
%.ttf.o %_ttf.h : %.ttf
|
||||
#-------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
%.bdf.o %_bdf.h : %.bdf
|
||||
#-------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
-include $(DEPENDS)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
endif
|
||||
#-------------------------------------------------------------------------------
|
18
README.md
Normal file
18
README.md
Normal file
@ -0,0 +1,18 @@
|
||||
# DRXUtil
|
||||
DRC/DRH experiments.
|
||||
|
||||
## Disclaimer
|
||||
Modifying the DRC firmware can cause permanent damage.
|
||||
No one but yourself is responsible for any sort of damage resulting from using this tool.
|
||||
|
||||
## Building
|
||||
For building you need:
|
||||
- [wut](https://github.com/devkitPro/wut)
|
||||
- [libmocha](https://github.com/wiiu-env/libmocha)
|
||||
- [wiiu-sdl2](https://github.com/GaryOderNichts/SDL/tree/wiiu-sdl2-2.26)
|
||||
- wiiu-sdl2_ttf
|
||||
|
||||
To build the project run `make`.
|
||||
|
||||
## See also
|
||||
- [drc-fw-patches](https://github.com/GaryOderNichts/drc-fw-patches)
|
BIN
data/fa-solid-900.ttf
Normal file
BIN
data/fa-solid-900.ttf
Normal file
Binary file not shown.
52912
data/ter-u32b.bdf
Normal file
52912
data/ter-u32b.bdf
Normal file
File diff suppressed because it is too large
Load Diff
287
source/Gfx.cpp
Normal file
287
source/Gfx.cpp
Normal file
@ -0,0 +1,287 @@
|
||||
#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("DRXUtil", 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;
|
||||
}
|
||||
|
||||
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
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);
|
||||
|
||||
}
|
104
source/ProcUI.cpp
Normal file
104
source/ProcUI.cpp
Normal file
@ -0,0 +1,104 @@
|
||||
#include "ProcUI.hpp"
|
||||
#include <coreinit/foreground.h>
|
||||
#include <coreinit/title.h>
|
||||
#include <proc_ui/procui.h>
|
||||
#include <sysapp/launch.h>
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr uint64_t kMiiMakerTitleID = 0x000500101004A000ull;
|
||||
constexpr uint64_t kHBLTitleID = 0x0005000013374842;
|
||||
|
||||
bool isRunning = true;
|
||||
bool isLegacyLoader = false;
|
||||
bool isHomeButtonMenuEnabled = true;
|
||||
|
||||
inline bool RunningFromLegacySetup()
|
||||
{
|
||||
uint64_t titleID = OSGetTitleID();
|
||||
|
||||
// Mask the region bits when comparing
|
||||
return (titleID & 0xFFFFFFFFFFFFF0FFull) == kMiiMakerTitleID || titleID == kHBLTitleID;
|
||||
}
|
||||
|
||||
uint32_t SaveCallback(void* context)
|
||||
{
|
||||
OSSavesDone_ReadyToRelease();
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t HomeButtonDeniedCallback(void* context)
|
||||
{
|
||||
if (!isHomeButtonMenuEnabled) {
|
||||
// TODO could show a denied image here
|
||||
} else {
|
||||
if (isLegacyLoader) {
|
||||
ProcUI::StopRunning();
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ProcUI::Init()
|
||||
{
|
||||
// Check if we're running from a legacy setup
|
||||
if (RunningFromLegacySetup()) {
|
||||
isLegacyLoader = true;
|
||||
OSEnableHomeButtonMenu(FALSE);
|
||||
}
|
||||
|
||||
isRunning = true;
|
||||
|
||||
ProcUIInitEx(SaveCallback, nullptr);
|
||||
ProcUIRegisterCallback(PROCUI_CALLBACK_HOME_BUTTON_DENIED, HomeButtonDeniedCallback, NULL, 100);
|
||||
}
|
||||
|
||||
void ProcUI::Shutdown()
|
||||
{
|
||||
isRunning = false;
|
||||
|
||||
// Legacy loaders require a title relaunch
|
||||
if (isLegacyLoader) {
|
||||
SYSRelaunchTitle(0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
bool ProcUI::IsRunning()
|
||||
{
|
||||
ProcUIStatus status = ProcUIProcessMessages(TRUE);
|
||||
if (status == PROCUI_STATUS_EXITING) {
|
||||
isRunning = false;
|
||||
} else if (status == PROCUI_STATUS_RELEASE_FOREGROUND) {
|
||||
ProcUIDrawDoneRelease();
|
||||
}
|
||||
|
||||
if (!isRunning) {
|
||||
ProcUIShutdown();
|
||||
}
|
||||
|
||||
return isRunning;
|
||||
}
|
||||
|
||||
void ProcUI::StopRunning()
|
||||
{
|
||||
// Legacy loaders can just return from main loop, otherwise we need a title to boot into
|
||||
if (isLegacyLoader) {
|
||||
isRunning = false;
|
||||
} else {
|
||||
SYSLaunchMenu();
|
||||
}
|
||||
}
|
||||
|
||||
void ProcUI::SetHomeButtonMenuEnabled(bool enabled)
|
||||
{
|
||||
isHomeButtonMenuEnabled = enabled;
|
||||
|
||||
if (!isLegacyLoader) {
|
||||
OSEnableHomeButtonMenu(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
|
17
source/ProcUI.hpp
Normal file
17
source/ProcUI.hpp
Normal file
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
namespace ProcUI
|
||||
{
|
||||
|
||||
void Init();
|
||||
|
||||
void Shutdown();
|
||||
|
||||
bool IsRunning();
|
||||
|
||||
void StopRunning();
|
||||
|
||||
void SetHomeButtonMenuEnabled(bool enabled);
|
||||
|
||||
} // namespace ProcUI
|
||||
|
2925
source/SDL_FontCache.c
Normal file
2925
source/SDL_FontCache.c
Normal file
File diff suppressed because it is too large
Load Diff
327
source/SDL_FontCache.h
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
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, "DRXUtil", Gfx::ALIGN_VERTICAL);
|
||||
Gfx::Print(Gfx::GetTextWidth(60, "DRXUtil") + 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
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:
|
||||
};
|
19
source/Utils.cpp
Normal file
19
source/Utils.cpp
Normal file
@ -0,0 +1,19 @@
|
||||
#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;
|
||||
}
|
||||
|
||||
}
|
22
source/Utils.hpp
Normal file
22
source/Utils.hpp
Normal file
@ -0,0 +1,22 @@
|
||||
#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);
|
||||
|
||||
}
|
157
source/main.cpp
Normal file
157
source/main.cpp
Normal file
@ -0,0 +1,157 @@
|
||||
#include "Gfx.hpp"
|
||||
#include "ProcUI.hpp"
|
||||
#include "screens/MainScreen.hpp"
|
||||
#include <memory>
|
||||
|
||||
#include <vpad/input.h>
|
||||
#include <padscore/kpad.h>
|
||||
#include <sndcore2/core.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
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[])
|
||||
{
|
||||
ProcUI::Init();
|
||||
|
||||
// call AXInit to stop already playing sounds
|
||||
AXInit();
|
||||
|
||||
KPADInit();
|
||||
WPADEnableURCC(TRUE);
|
||||
|
||||
Gfx::Init();
|
||||
|
||||
std::unique_ptr<Screen> mainScreen = std::make_unique<MainScreen>();
|
||||
|
||||
while (ProcUI::IsRunning()) {
|
||||
VPADStatus input{};
|
||||
VPADRead(VPAD_CHAN_0, &input, 1, nullptr);
|
||||
UpdatePads(&input);
|
||||
|
||||
if (!mainScreen->Update(input)) {
|
||||
ProcUI::StopRunning();
|
||||
}
|
||||
|
||||
mainScreen->Draw();
|
||||
Gfx::Render();
|
||||
}
|
||||
|
||||
mainScreen.reset();
|
||||
|
||||
Gfx::Shutdown();
|
||||
|
||||
AXQuit();
|
||||
|
||||
ProcUI::Shutdown();
|
||||
return 0;
|
||||
}
|
43
source/screens/AboutScreen.cpp
Normal file
43
source/screens/AboutScreen.cpp
Normal file
@ -0,0 +1,43 @@
|
||||
#include "AboutScreen.hpp"
|
||||
|
||||
AboutScreen::AboutScreen()
|
||||
{
|
||||
mCreditList.push_back({"Developers:", "GaryOderNichts"});
|
||||
|
||||
mFontList.push_back({"Main Font:", "Wii U System Font"});
|
||||
mFontList.push_back({"Icon Font:", "FontAwesome"});
|
||||
mFontList.push_back({"Monospace Font:", "Terminus Font"});
|
||||
|
||||
mLinkList.push_back({"GitHub:", ""});
|
||||
mLinkList.push_back({"", {"github.com/GaryOderNichts/DRXUtil", true}});
|
||||
}
|
||||
|
||||
AboutScreen::~AboutScreen()
|
||||
{
|
||||
}
|
||||
|
||||
void AboutScreen::Draw()
|
||||
{
|
||||
DrawTopBar("AboutScreen");
|
||||
|
||||
int yOff = 128;
|
||||
yOff = DrawHeader(32, yOff, 896, 0xf121, "Credits");
|
||||
yOff = DrawList(32, yOff, 896, mCreditList);
|
||||
yOff = DrawHeader(32, yOff, 896, 0xf031, "Fonts");
|
||||
yOff = DrawList(32, yOff, 896, mFontList);
|
||||
|
||||
yOff = 128;
|
||||
yOff = DrawHeader(992, yOff, 896, 0xf08e, "Links");
|
||||
yOff = DrawList(992, yOff, 896, mLinkList);
|
||||
|
||||
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
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 mCreditList;
|
||||
ScreenList mFontList;
|
||||
ScreenList mLinkList;
|
||||
};
|
471
source/screens/FlashScreen.cpp
Normal file
471
source/screens/FlashScreen.cpp
Normal file
@ -0,0 +1,471 @@
|
||||
#include "FlashScreen.hpp"
|
||||
#include "Gfx.hpp"
|
||||
#include "ProcUI.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include <cstdio>
|
||||
|
||||
#include <coreinit/mcp.h>
|
||||
#include <coreinit/thread.h>
|
||||
#include <coreinit/filesystem_fsa.h>
|
||||
#include <nsysccr/cdc.h>
|
||||
#include <nsysccr/cfg.h>
|
||||
#include <nn/ccr.h>
|
||||
|
||||
namespace {
|
||||
|
||||
bool GetDRCFirmwarePath(std::string& path)
|
||||
{
|
||||
int32_t handle = MCP_Open();
|
||||
if (handle < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
alignas(0x40) MCPTitleListType title;
|
||||
uint32_t titleCount = 0;
|
||||
MCPError error = MCP_TitleListByAppType(handle, MCP_APP_TYPE_DRC_FIRMWARE, &titleCount, &title, sizeof(title));
|
||||
MCP_Close(handle);
|
||||
|
||||
if (error != 0 || titleCount != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
path = std::string(&title.path[0]) + "/content/drc_fw.bin";
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReadFirmwareHeader(const std::string& path, FlashScreen::FirmwareHeader& header)
|
||||
{
|
||||
FILE* f = fopen(path.c_str(), "rb");
|
||||
if (!f) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fread(&header, 1, sizeof(header), f) != sizeof(header)) {
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CopyFile(const std::string& srcPath, const std::string& dstPath)
|
||||
{
|
||||
FILE* inf = fopen(srcPath.c_str(), "rb");
|
||||
if (!inf) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FILE* outf = fopen(dstPath.c_str(), "wb");
|
||||
if (!outf) {
|
||||
fclose(inf);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t buf[4096];
|
||||
while (!feof(inf)) {
|
||||
size_t read = fread(buf, 1, sizeof(buf), inf);
|
||||
fwrite(buf, 1, read, outf);
|
||||
}
|
||||
|
||||
fclose(inf);
|
||||
fclose(outf);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CaffeineInvalidate()
|
||||
{
|
||||
CCRCDCSoftwareVersion version;
|
||||
CCRCDCSoftwareGetVersion(CCR_CDC_DESTINATION_DRC0, &version);
|
||||
|
||||
// Only newer versions have caffeine
|
||||
if (version.runningVersion >= 0x180a0000) {
|
||||
return CCRSysCaffeineSetCaffeineSlot(0xff) == 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WaitForEeprom(uint32_t drcSlot)
|
||||
{
|
||||
uint8_t val;
|
||||
OSTime startTime = OSGetSystemTime();
|
||||
while (CCRCFGGetCachedEeprom(drcSlot, 0, &val, sizeof(val)) == -1) {
|
||||
// 2 second timeout
|
||||
if (OSTicksToSeconds(OSGetSystemTime() - startTime) > 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
OSSleepTicks(OSMillisecondsToTicks(200));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReattachDRC(CCRCDCDestination dest, CCRCDCDrcState targetState, BOOL unknown)
|
||||
{
|
||||
// Get the current DRC state
|
||||
CCRCDCDrcState state;
|
||||
int32_t res = CCRCDCSysGetDrcState(dest, &state);
|
||||
if (res != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Not sure what state 3 is
|
||||
if (state == CCR_CDC_DRC_STATE_UNK3) {
|
||||
state = CCR_CDC_DRC_STATE_ACTIVE;
|
||||
}
|
||||
|
||||
// Nothing to do if we're already in the target state
|
||||
if (state == targetState) {
|
||||
return true;
|
||||
}
|
||||
|
||||
__CCRSysInitReattach(dest - CCR_CDC_DESTINATION_DRC0);
|
||||
|
||||
// Set target state
|
||||
state = targetState;
|
||||
res = CCRCDCSysSetDrcState(dest, &state);
|
||||
if (res != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Wait for the DRC to reattach
|
||||
res = __CCRSysWaitReattach(dest - CCR_CDC_DESTINATION_DRC0, unknown);
|
||||
if (res != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Wait for EEPROM
|
||||
if (!WaitForEeprom(dest - CCR_CDC_DESTINATION_DRC0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if we're in the state we want
|
||||
res = CCRCDCSysGetDrcState(dest, &state);
|
||||
if (res != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (state != targetState) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AbortUpdate(CCRCDCDestination dest)
|
||||
{
|
||||
OSTime startTime = OSGetSystemTime();
|
||||
while (CCRCDCSoftwareAbort(dest) != 0) {
|
||||
// 3 second timeout
|
||||
if (OSTicksToSeconds(OSGetSystemTime() - startTime) > 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
OSSleepTicks(OSMillisecondsToTicks(200));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SoftwareUpdateCallback(IOSError error, void* arg)
|
||||
{
|
||||
FlashScreen* flashScreen = static_cast<FlashScreen*>(arg);
|
||||
|
||||
flashScreen->OnUpdateCompleted(error);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
FlashScreen::FlashScreen()
|
||||
: mFileEntries({
|
||||
{FILE_ORIGINAL, {0xf187, "Original Firmware"}},
|
||||
{FILE_SDCARD, {0xf7c2, "From SD Card (\"sd:/drc_fw.bin\")"}},
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
FlashScreen::~FlashScreen()
|
||||
{
|
||||
}
|
||||
|
||||
void FlashScreen::Draw()
|
||||
{
|
||||
DrawTopBar("FlashScreen");
|
||||
|
||||
switch (mState)
|
||||
{
|
||||
case STATE_SELECT_FILE: {
|
||||
for (FileID id = FILE_ORIGINAL; id <= FILE_SDCARD; id = static_cast<FileID>(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, mFileEntries[id].icon);
|
||||
Gfx::Print(128 + 8, yOff + 150 / 2, 60, Gfx::COLOR_TEXT, mFileEntries[id].name, Gfx::ALIGN_VERTICAL);
|
||||
|
||||
if (id == mFile) {
|
||||
Gfx::DrawRect(0, yOff, Gfx::SCREEN_WIDTH, 150, 8, Gfx::COLOR_HIGHLIGHTED);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case STATE_CONFIRM: {
|
||||
Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_TEXT,
|
||||
Utils::sprintf("Are you sure?\n"
|
||||
"About to flash: %s", mFileEntries[mFile].name),
|
||||
Gfx::ALIGN_CENTER);
|
||||
break;
|
||||
}
|
||||
case STATE_PREPARE: {
|
||||
Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_TEXT, "Preparing...", Gfx::ALIGN_CENTER);
|
||||
break;
|
||||
}
|
||||
case STATE_CONFIRM2: {
|
||||
Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_ERROR,
|
||||
Utils::sprintf("Are you really really really sure?\n"
|
||||
"About to flash firmware version 0x%08x.\n"
|
||||
"Flashing a firmware can do permanent damage!!!\n", mFirmwareHeader.version),
|
||||
Gfx::ALIGN_CENTER);
|
||||
break;
|
||||
}
|
||||
case STATE_UPDATE: {
|
||||
Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_TEXT, "Starting update...", Gfx::ALIGN_CENTER);
|
||||
break;
|
||||
}
|
||||
case STATE_FLASHING: {
|
||||
Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2 - 32, 64, Gfx::COLOR_TEXT, Utils::sprintf("Flashing... %d%%", mFlashingProgress), Gfx::ALIGN_CENTER);
|
||||
Gfx::DrawRect(64, Gfx::SCREEN_HEIGHT / 2 + 32, Gfx::SCREEN_WIDTH - 128, 64, 5, Gfx::COLOR_ACCENT);
|
||||
Gfx::DrawRectFilled(64, Gfx::SCREEN_HEIGHT / 2 + 32, (Gfx::SCREEN_WIDTH - 128) * (mFlashingProgress / 100.0f), 64, Gfx::COLOR_ACCENT);
|
||||
break;
|
||||
}
|
||||
case STATE_ACTIVATE: {
|
||||
Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_TEXT, "Activating firmware...", Gfx::ALIGN_CENTER);
|
||||
break;
|
||||
}
|
||||
case STATE_DONE: {
|
||||
Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_TEXT,
|
||||
Utils::sprintf("Done!\n"
|
||||
"Flashed firmware version:\n0x%08x (%s)", mFirmwareHeader.version, mFileEntries[mFile].name),
|
||||
Gfx::ALIGN_CENTER);
|
||||
break;
|
||||
}
|
||||
case STATE_ERROR: {
|
||||
Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_ERROR, "Error:\n" + mErrorString, Gfx::ALIGN_CENTER);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mState == STATE_SELECT_FILE) {
|
||||
DrawBottomBar("\ue07d Navigate", "\ue044 Exit", "\ue000 Confirm / \ue001 Back");
|
||||
} else if (mState == STATE_CONFIRM || mState == STATE_CONFIRM2) {
|
||||
DrawBottomBar(nullptr, "\ue044 Exit", "\ue000 Confirm / \ue001 Back");
|
||||
} else if (mState == STATE_PREPARE || mState == STATE_UPDATE || mState == STATE_FLASHING || mState == STATE_ACTIVATE) {
|
||||
DrawBottomBar(nullptr, "Please wait...", nullptr);
|
||||
} else {
|
||||
DrawBottomBar(nullptr, nullptr, "\ue001 Back");
|
||||
}
|
||||
}
|
||||
|
||||
bool FlashScreen::Update(VPADStatus& input)
|
||||
{
|
||||
switch (mState)
|
||||
{
|
||||
case STATE_SELECT_FILE: {
|
||||
if (input.trigger & VPAD_BUTTON_B) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (input.trigger & VPAD_BUTTON_A) {
|
||||
mState = STATE_CONFIRM;
|
||||
break;
|
||||
}
|
||||
|
||||
if (input.trigger & VPAD_BUTTON_DOWN) {
|
||||
if (mFile < FILE_SDCARD) {
|
||||
mFile = static_cast<FileID>(mFile + 1);
|
||||
}
|
||||
} else if (input.trigger & VPAD_BUTTON_UP) {
|
||||
if (mFile > FILE_ORIGINAL) {
|
||||
mFile = static_cast<FileID>(mFile - 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case STATE_CONFIRM:
|
||||
case STATE_CONFIRM2: {
|
||||
if (input.trigger & VPAD_BUTTON_B) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (input.trigger & VPAD_BUTTON_A) {
|
||||
mState = (mState == STATE_CONFIRM) ? STATE_PREPARE : STATE_UPDATE;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case STATE_PREPARE: {
|
||||
ProcUI::SetHomeButtonMenuEnabled(false);
|
||||
|
||||
std::string originalFirmwarePath;
|
||||
if (!GetDRCFirmwarePath(originalFirmwarePath)) {
|
||||
mErrorString = "Failed to get original DRC firmware path";
|
||||
mState = STATE_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
// Replace absolute path with devoptab prefix
|
||||
std::size_t prefixPos = originalFirmwarePath.find("/vol/storage_mlc01");
|
||||
if (prefixPos != 0) {
|
||||
mErrorString = "Invalid firmware path";
|
||||
mState = STATE_ERROR;
|
||||
break;
|
||||
}
|
||||
std::string doptPath = originalFirmwarePath;
|
||||
doptPath.replace(prefixPos, sizeof("/vol/storage_mlc01") - 1, "storage_mlc01:");
|
||||
|
||||
FirmwareHeader originalFirmwareHeader;
|
||||
if (!ReadFirmwareHeader(doptPath, originalFirmwareHeader)) {
|
||||
mErrorString = "Failed to read original DRC firmware header";
|
||||
mState = STATE_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
if (mFile == FILE_ORIGINAL) {
|
||||
mFirmwarePath = originalFirmwarePath;
|
||||
mFirmwareHeader = originalFirmwareHeader;
|
||||
} else if (mFile == FILE_SDCARD) {
|
||||
if (!ReadFirmwareHeader("/vol/external01/drc_fw.bin", mFirmwareHeader)) {
|
||||
mErrorString = "Failed to read DRC firmware header";
|
||||
mState = STATE_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
// Don't allow downgrading lower than the version on NAND,
|
||||
// otherwise this might cause bricks without flashing the language files?
|
||||
if (mFirmwareHeader.version < originalFirmwareHeader.version) {
|
||||
mErrorString = Utils::sprintf("Not allowing versions lower than version on NAND.\n(Firmware 0x%08x Original 0x%08x)", mFirmwareHeader.version, originalFirmwareHeader.version);
|
||||
mState = STATE_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
// Copy to MLC so IOS-PAD can install it
|
||||
mFirmwarePath = "/vol/storage_mlc01/usr/tmp/drc_fw.bin";
|
||||
if (!CopyFile("/vol/external01/drc_fw.bin", "storage_mlc01:/usr/tmp/drc_fw.bin")) {
|
||||
mErrorString = "Failed to copy firmware to MLC";
|
||||
mState = STATE_ERROR;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
mState = STATE_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
mState = STATE_CONFIRM2;
|
||||
break;
|
||||
}
|
||||
case STATE_UPDATE: {
|
||||
if (!CaffeineInvalidate()) {
|
||||
mErrorString = "Failed to invalidate caffeine.";
|
||||
mState = STATE_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
// Abort any potential pending software updates
|
||||
CCRCDCSoftwareAbort(CCR_CDC_DESTINATION_DRC0);
|
||||
|
||||
// Reattach the DRC in update mode
|
||||
if (!ReattachDRC(CCR_CDC_DESTINATION_DRC0, CCR_CDC_DRC_STATE_UPDATE, 0)) {
|
||||
ReattachDRC(CCR_CDC_DESTINATION_DRC0, CCR_CDC_DRC_STATE_ACTIVE, 0);
|
||||
mErrorString = "Failed to reattach DRC in update mode.";
|
||||
mState = STATE_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
mFlashingProgress = 0;
|
||||
mUpdateComplete = false;
|
||||
mUpdateResult = 0;
|
||||
if (CCRCDCSoftwareUpdate(CCR_CDC_DESTINATION_DRC0, mFirmwarePath.c_str(), SoftwareUpdateCallback, this) != 0) {
|
||||
AbortUpdate(CCR_CDC_DESTINATION_DRC0);
|
||||
ReattachDRC(CCR_CDC_DESTINATION_DRC0, CCR_CDC_DRC_STATE_ACTIVE, 0);
|
||||
mErrorString = "Failed to start software update.";
|
||||
mState = STATE_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
mState = STATE_FLASHING;
|
||||
break;
|
||||
}
|
||||
case STATE_FLASHING: {
|
||||
// Update progress
|
||||
CCRCDCFWInfo fwInfo{};
|
||||
if (CCRCDCGetFWInfo(CCR_CDC_DESTINATION_DRC0, &fwInfo) == 0) {
|
||||
mFlashingProgress = fwInfo.updateProgress;
|
||||
}
|
||||
|
||||
OSSleepTicks(OSMillisecondsToTicks(200));
|
||||
|
||||
// Check if update complete
|
||||
if (mUpdateComplete) {
|
||||
if (mUpdateResult == IOS_ERROR_OK) {
|
||||
mState = STATE_ACTIVATE;
|
||||
} else {
|
||||
AbortUpdate(CCR_CDC_DESTINATION_DRC0);
|
||||
ReattachDRC(CCR_CDC_DESTINATION_DRC0, CCR_CDC_DRC_STATE_ACTIVE, 0);
|
||||
mErrorString = "Software update failed.";
|
||||
mState = STATE_ERROR;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case STATE_ACTIVATE: {
|
||||
// Activate the newly flashed firmware
|
||||
if (CCRCDCSoftwareActivate(CCR_CDC_DESTINATION_DRC0) != 0) {
|
||||
AbortUpdate(CCR_CDC_DESTINATION_DRC0);
|
||||
ReattachDRC(CCR_CDC_DESTINATION_DRC0, CCR_CDC_DRC_STATE_ACTIVE, 0);
|
||||
mErrorString = "Failed to activate software update.";
|
||||
mState = STATE_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
// Put the gamepad back into active mode
|
||||
OSTime startTime = OSGetSystemTime();
|
||||
while (!ReattachDRC(CCR_CDC_DESTINATION_DRC0, CCR_CDC_DRC_STATE_ACTIVE, 0)) {
|
||||
// 10 second timeout
|
||||
if (OSTicksToSeconds(OSGetSystemTime() - startTime) > 10) {
|
||||
// At this point we don't really care if it times out or not
|
||||
break;
|
||||
}
|
||||
|
||||
OSSleepTicks(OSMillisecondsToTicks(1000));
|
||||
}
|
||||
|
||||
mState = STATE_DONE;
|
||||
break;
|
||||
}
|
||||
case STATE_DONE: {
|
||||
if (input.trigger & VPAD_BUTTON_B) {
|
||||
ProcUI::SetHomeButtonMenuEnabled(true);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case STATE_ERROR: {
|
||||
if (input.trigger & VPAD_BUTTON_B) {
|
||||
ProcUI::SetHomeButtonMenuEnabled(true);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FlashScreen::OnUpdateCompleted(int32_t result)
|
||||
{
|
||||
mUpdateComplete = true;
|
||||
mUpdateResult = result;
|
||||
}
|
56
source/screens/FlashScreen.hpp
Normal file
56
source/screens/FlashScreen.hpp
Normal file
@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include "Screen.hpp"
|
||||
#include <map>
|
||||
|
||||
class FlashScreen : public Screen
|
||||
{
|
||||
public:
|
||||
struct FirmwareHeader {
|
||||
uint32_t version;
|
||||
uint32_t blockSize;
|
||||
uint32_t sequencePerSession;
|
||||
uint32_t imageSize;
|
||||
};
|
||||
|
||||
public:
|
||||
FlashScreen();
|
||||
virtual ~FlashScreen();
|
||||
|
||||
void Draw();
|
||||
|
||||
bool Update(VPADStatus& input);
|
||||
|
||||
void OnUpdateCompleted(int32_t result);
|
||||
|
||||
private:
|
||||
enum State {
|
||||
STATE_SELECT_FILE,
|
||||
STATE_CONFIRM,
|
||||
STATE_PREPARE,
|
||||
STATE_CONFIRM2,
|
||||
STATE_UPDATE,
|
||||
STATE_FLASHING,
|
||||
STATE_ACTIVATE,
|
||||
STATE_DONE,
|
||||
STATE_ERROR,
|
||||
} mState = STATE_SELECT_FILE;
|
||||
|
||||
enum FileID {
|
||||
FILE_ORIGINAL,
|
||||
FILE_SDCARD,
|
||||
} mFile = FILE_ORIGINAL;
|
||||
struct FileEntry {
|
||||
uint16_t icon;
|
||||
const char* name;
|
||||
};
|
||||
std::map<FileID, FileEntry> mFileEntries;
|
||||
|
||||
std::string mErrorString;
|
||||
std::string mFirmwarePath;
|
||||
FirmwareHeader mFirmwareHeader;
|
||||
|
||||
int32_t mFlashingProgress;
|
||||
bool mUpdateComplete;
|
||||
int32_t mUpdateResult;
|
||||
};
|
170
source/screens/InfoScreen.cpp
Normal file
170
source/screens/InfoScreen.cpp
Normal file
@ -0,0 +1,170 @@
|
||||
#include "InfoScreen.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include <span>
|
||||
|
||||
#include <nsysccr/cdc.h>
|
||||
#include <nsysccr/cfg.h>
|
||||
|
||||
namespace {
|
||||
|
||||
bool GetEepromValue(uint32_t offset, std::span<uint8_t> value)
|
||||
{
|
||||
uint8_t data[value.size() + 2];
|
||||
if (CCRCFGGetCachedEeprom(0, offset, data, value.size() + 2) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t crc = (uint16_t) data[value.size() + 1] << 8 | data[value.size()];
|
||||
if (CCRCDCCalcCRC16(data, value.size()) != crc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::copy(data, data + value.size(), value.begin());
|
||||
return true;
|
||||
}
|
||||
|
||||
// from gamepad firmare @0x000b2990
|
||||
const char* kBoardMainVersions[] = {
|
||||
"DK1",
|
||||
"DK1 / EP / DK2",
|
||||
"DP1",
|
||||
"DP2",
|
||||
"DK3",
|
||||
"DK4",
|
||||
"PreDP3 / DP3",
|
||||
"DK5",
|
||||
"DP4",
|
||||
"DKMP",
|
||||
"DP5",
|
||||
"MASS",
|
||||
"DKMP2",
|
||||
"DRC-I",
|
||||
"DKTVMP",
|
||||
};
|
||||
|
||||
// from gamepad firmare @0x000b29cc
|
||||
const char* kBoardSubVersions[] = {
|
||||
"DK1 / EP / DK2",
|
||||
"DP1 / DK3",
|
||||
"DK4",
|
||||
"DP3",
|
||||
"DK5",
|
||||
"DP4",
|
||||
"DKMP",
|
||||
"DP5",
|
||||
"MASS",
|
||||
"DKMP2",
|
||||
"DRC-I",
|
||||
"DKTVMP"
|
||||
};
|
||||
|
||||
// from gamepad firmare @0x000b29fc
|
||||
const char* kRegionStrings[] = {
|
||||
"JAPAN",
|
||||
"AMERICA",
|
||||
"EUROPE",
|
||||
"CHINA",
|
||||
"SOUTH KOREA",
|
||||
"TAIWAN",
|
||||
"AUSTRALIA",
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
InfoScreen::InfoScreen()
|
||||
{
|
||||
CCRCDCSoftwareVersion softwareVersion;
|
||||
if (CCRCDCSoftwareGetVersion(CCR_CDC_DESTINATION_DRC0, &softwareVersion) == 0) {
|
||||
uint32_t v = softwareVersion.runningVersion;
|
||||
mDRCList.push_back({"Running Version:", Utils::sprintf("%d.%d.%d", v >> 24 & 0xff, v >> 16 & 0xff, v & 0xffff)});
|
||||
mDRCList.push_back({"", {Utils::sprintf("(0x%08x)", v), true}});
|
||||
v = softwareVersion.activeVersion;
|
||||
mDRCList.push_back({"Active Version:", Utils::sprintf("%d.%d.%d", v >> 24 & 0xff, v >> 16 & 0xff, v & 0xffff)});
|
||||
mDRCList.push_back({"", {Utils::sprintf("(0x%08x)", v), true}});
|
||||
} else {
|
||||
mDRCList.push_back({"CCRCDCSoftwareGetVersion failed", ""});
|
||||
}
|
||||
|
||||
uint8_t boardInfo;
|
||||
if (GetEepromValue(0x100, std::span(std::addressof(boardInfo), 1))) {
|
||||
uint8_t mainVersion = boardInfo & 0xf;
|
||||
uint8_t subVersion = boardInfo >> 4;
|
||||
mDRCList.push_back({"Board Version:", Utils::sprintf("%d.%d (0x%02x)", mainVersion, subVersion, boardInfo)});
|
||||
mDRCList.push_back({"", Utils::sprintf("(%s / %s)",
|
||||
mainVersion < 0xf ? kBoardMainVersions[mainVersion] : "UNKNOWN",
|
||||
subVersion < 0xc ? kBoardSubVersions[subVersion] : "UNKNOWN")});
|
||||
} else {
|
||||
mDRCList.push_back({"GetEepromValue failed", ""});
|
||||
}
|
||||
|
||||
uint8_t region;
|
||||
if (GetEepromValue(0x103, std::span(std::addressof(region), 1))) {
|
||||
mDRCList.push_back({"Region:", Utils::sprintf("%s (0x%02x)",
|
||||
region < 0x7 ? kRegionStrings[region] : "UNKNOWN", region)});
|
||||
} else {
|
||||
mDRCList.push_back({"GetRegion failed", ""});
|
||||
}
|
||||
|
||||
if (CCRCDCSoftwareGetVersion(CCR_CDC_DESTINATION_DRH, &softwareVersion) == 0) {
|
||||
uint32_t v = softwareVersion.runningVersion;
|
||||
mDRHList.push_back({"Running Version:", Utils::sprintf("%d.%d.%d", v >> 24 & 0xff, v >> 16 & 0xff, v & 0xffff)});
|
||||
mDRHList.push_back({"", {Utils::sprintf("(0x%08x)", v), true}});
|
||||
v = softwareVersion.activeVersion;
|
||||
mDRHList.push_back({"Active Version:", Utils::sprintf("%d.%d.%d", v >> 24 & 0xff, v >> 16 & 0xff, v & 0xffff)});
|
||||
mDRHList.push_back({"", {Utils::sprintf("(0x%08x)", v), true}});
|
||||
} else {
|
||||
mDRHList.push_back({"CCRCDCSoftwareGetVersion failed", ""});
|
||||
}
|
||||
|
||||
uint32_t extId;
|
||||
if (CCRCDCSoftwareGetExtId(CCR_CDC_DESTINATION_DRC0, CCR_CDC_EXT_LANGUAGE, &extId) == 0) {
|
||||
mExtIdList.push_back({"Language:", {Utils::sprintf("0x%08x", extId), true}});
|
||||
mExtIdList.push_back({"", Utils::sprintf("(version: %04d bank: %02d)", extId >> 8 & 0xffff, extId >> 24)});
|
||||
} else {
|
||||
mExtIdList.push_back({"CCRCDCSoftwareGetExtId(CCR_CDC_EXT_LANGUAGE) failed", ""});
|
||||
}
|
||||
|
||||
if (CCRCDCSoftwareGetExtId(CCR_CDC_DESTINATION_DRC0, CCR_CDC_EXT_RC_DATABASE, &extId) == 0) {
|
||||
mExtIdList.push_back({"RC Database:", {Utils::sprintf("0x%08x", extId), true}});
|
||||
} else {
|
||||
mExtIdList.push_back({"CCRCDCSoftwareGetExtId(CCR_CDC_EXT_RC_DATABASE) failed", ""});
|
||||
}
|
||||
|
||||
for (int i = CCR_CDC_EXT_UNK2; i <= CCR_CDC_EXT_UNK4; i++) {
|
||||
if (CCRCDCSoftwareGetExtId(CCR_CDC_DESTINATION_DRC0, (CCRCDCExt) i, &extId) == 0) {
|
||||
mExtIdList.push_back({Utils::sprintf("ID %d:", i), {Utils::sprintf("0x%08x", extId), true}});
|
||||
} else {
|
||||
mExtIdList.push_back({Utils::sprintf("CCRCDCSoftwareGetExtId(%d) failed", i), ""});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InfoScreen::~InfoScreen()
|
||||
{
|
||||
}
|
||||
|
||||
void InfoScreen::Draw()
|
||||
{
|
||||
DrawTopBar("InfoScreen");
|
||||
|
||||
int yOff = 128;
|
||||
yOff = DrawHeader(32, yOff, 896, 0xf11b, "DRC Info");
|
||||
yOff = DrawList(32, yOff, 896, mDRCList);
|
||||
yOff = DrawHeader(32, yOff, 896, 0xf0cb, "DRC Ext IDs");
|
||||
yOff = DrawList(32, yOff, 896, mExtIdList);
|
||||
|
||||
yOff = 128;
|
||||
yOff = DrawHeader(992, yOff, 896, 0xf2db, "DRH Info");
|
||||
yOff = DrawList(992, yOff, 896, mDRHList);
|
||||
|
||||
DrawBottomBar(nullptr, "\ue044 Exit", "\ue001 Back");
|
||||
}
|
||||
|
||||
bool InfoScreen::Update(VPADStatus& input)
|
||||
{
|
||||
if (input.trigger & VPAD_BUTTON_B) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
20
source/screens/InfoScreen.hpp
Normal file
20
source/screens/InfoScreen.hpp
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "Screen.hpp"
|
||||
|
||||
class InfoScreen : public Screen
|
||||
{
|
||||
public:
|
||||
InfoScreen();
|
||||
virtual ~InfoScreen();
|
||||
|
||||
void Draw();
|
||||
|
||||
bool Update(VPADStatus& input);
|
||||
|
||||
private:
|
||||
ScreenList mDRCList;
|
||||
ScreenList mExtIdList;
|
||||
|
||||
ScreenList mDRHList;
|
||||
};
|
152
source/screens/MainScreen.cpp
Normal file
152
source/screens/MainScreen.cpp
Normal file
@ -0,0 +1,152 @@
|
||||
#include "MainScreen.hpp"
|
||||
#include "MenuScreen.hpp"
|
||||
#include "Gfx.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <mocha/mocha.h>
|
||||
#include <coreinit/filesystem_fsa.h>
|
||||
#include <nn/ccr.h>
|
||||
|
||||
MainScreen::~MainScreen()
|
||||
{
|
||||
if (mState > STATE_INIT_CCR_SYS) {
|
||||
CCRSysExit();
|
||||
}
|
||||
|
||||
if (mState > STATE_MOUNT_FS) {
|
||||
Mocha_UnmountFS("storage_mlc01");
|
||||
}
|
||||
|
||||
if (mState > STATE_INIT_MOCHA) {
|
||||
Mocha_DeInitLibrary();
|
||||
}
|
||||
}
|
||||
|
||||
void MainScreen::Draw()
|
||||
{
|
||||
Gfx::Clear(Gfx::COLOR_BACKGROUND);
|
||||
|
||||
if (mMenuScreen) {
|
||||
mMenuScreen->Draw();
|
||||
return;
|
||||
}
|
||||
|
||||
DrawTopBar(nullptr);
|
||||
|
||||
switch (mState) {
|
||||
case STATE_INIT:
|
||||
break;
|
||||
case STATE_INIT_CCR_SYS:
|
||||
DrawStatus("Initializing CCRSys...");
|
||||
break;
|
||||
case STATE_INIT_MOCHA:
|
||||
if (mStateFailure) {
|
||||
DrawStatus("Failed to initialize mocha!\nMake sure to update or install Tiramisu/Aroma.", Gfx::COLOR_ERROR);
|
||||
break;
|
||||
}
|
||||
|
||||
DrawStatus("Initializing mocha...");
|
||||
break;
|
||||
case STATE_MOUNT_FS:
|
||||
if (mStateFailure) {
|
||||
DrawStatus("Failed to mount FS!", Gfx::COLOR_ERROR);
|
||||
break;
|
||||
}
|
||||
|
||||
DrawStatus("Mounting FS...");
|
||||
break;
|
||||
case STATE_PATCH_IOS:
|
||||
if (mStateFailure) {
|
||||
DrawStatus("Failed to patch IOS!", Gfx::COLOR_ERROR);
|
||||
break;
|
||||
}
|
||||
|
||||
DrawStatus("Patching IOS...");
|
||||
break;
|
||||
case STATE_LOAD_MENU:
|
||||
DrawStatus("Loading menu...");
|
||||
break;
|
||||
case STATE_IN_MENU:
|
||||
break;
|
||||
}
|
||||
|
||||
DrawBottomBar(mStateFailure ? nullptr : "Please wait...", mStateFailure ? "\ue044 Exit" : nullptr, nullptr);
|
||||
}
|
||||
|
||||
bool MainScreen::Update(VPADStatus& input)
|
||||
{
|
||||
if (mMenuScreen) {
|
||||
if (!mMenuScreen->Update(input)) {
|
||||
// menu wants to exit
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
MochaUtilsStatus status;
|
||||
switch (mState) {
|
||||
case STATE_INIT:
|
||||
mState = STATE_INIT_CCR_SYS;
|
||||
break;
|
||||
case STATE_INIT_CCR_SYS:
|
||||
CCRSysInit();
|
||||
mState = STATE_INIT_MOCHA;
|
||||
break;
|
||||
case STATE_INIT_MOCHA:
|
||||
status = Mocha_InitLibrary();
|
||||
if (status != MOCHA_RESULT_SUCCESS) {
|
||||
mStateFailure = true;
|
||||
break;
|
||||
}
|
||||
|
||||
mState = STATE_MOUNT_FS;
|
||||
break;
|
||||
case STATE_MOUNT_FS:
|
||||
// We need access to the MLC to read/write DRC firmware
|
||||
status = Mocha_MountFS("storage_mlc01", nullptr, "/vol/storage_mlc01");
|
||||
if (status != MOCHA_RESULT_SUCCESS) {
|
||||
mStateFailure = true;
|
||||
break;
|
||||
}
|
||||
|
||||
mState = STATE_PATCH_IOS;
|
||||
break;
|
||||
case STATE_PATCH_IOS:
|
||||
uint32_t op;
|
||||
if (Mocha_IOSUKernelRead32(0x11f53cf4, &op) != MOCHA_RESULT_SUCCESS) {
|
||||
mStateFailure = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (op == 0xeb00bd82) {
|
||||
// nop IOSU version check
|
||||
status = Mocha_IOSUKernelWrite32(0x11f53cf4, 0xe3a00001);
|
||||
if (status != MOCHA_RESULT_SUCCESS) {
|
||||
mStateFailure = true;
|
||||
break;
|
||||
}
|
||||
} else if (op == 0xe3a00001) {
|
||||
// already patched
|
||||
} else {
|
||||
mStateFailure = true;
|
||||
break;
|
||||
}
|
||||
|
||||
mState = STATE_LOAD_MENU;
|
||||
break;
|
||||
case STATE_LOAD_MENU:
|
||||
mMenuScreen = std::make_unique<MenuScreen>();
|
||||
mState = STATE_IN_MENU;
|
||||
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
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_CCR_SYS,
|
||||
STATE_INIT_MOCHA,
|
||||
STATE_MOUNT_FS,
|
||||
STATE_PATCH_IOS,
|
||||
STATE_LOAD_MENU,
|
||||
STATE_IN_MENU,
|
||||
} mState = STATE_INIT;
|
||||
bool mStateFailure = false;
|
||||
|
||||
std::unique_ptr<Screen> mMenuScreen;
|
||||
};
|
88
source/screens/MenuScreen.cpp
Normal file
88
source/screens/MenuScreen.cpp
Normal file
@ -0,0 +1,88 @@
|
||||
#include "MenuScreen.hpp"
|
||||
#include "Gfx.hpp"
|
||||
#include "AboutScreen.hpp"
|
||||
#include "FlashScreen.hpp"
|
||||
#include "InfoScreen.hpp"
|
||||
#include "SetRegionScreen.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
MenuScreen::MenuScreen()
|
||||
: mEntries({
|
||||
{ MENU_ID_INFO, { 0xf085, "Show DRC/DRH information" }},
|
||||
{ MENU_ID_FLASH, { 0xf1c9, "Flash firmware" }},
|
||||
{ MENU_ID_SET_REGION, { 0xf0ac, "Set region" }},
|
||||
{ MENU_ID_ABOUT, { 0xf05a, "About DRXUtil" }},
|
||||
// { MENU_ID_EXIT, { 0xf057, "Exit" }},
|
||||
})
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
MenuScreen::~MenuScreen()
|
||||
{
|
||||
}
|
||||
|
||||
void MenuScreen::Draw()
|
||||
{
|
||||
if (mSubscreen) {
|
||||
mSubscreen->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, mEntries[id].icon);
|
||||
Gfx::Print(128 + 8, yOff + 150 / 2, 60, Gfx::COLOR_TEXT, mEntries[id].name, Gfx::ALIGN_VERTICAL);
|
||||
|
||||
if (id == mSelected) {
|
||||
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 (mSubscreen) {
|
||||
if (!mSubscreen->Update(input)) {
|
||||
// subscreen wants to exit
|
||||
mSubscreen.reset();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (input.trigger & VPAD_BUTTON_DOWN) {
|
||||
if (mSelected < MENU_ID_MAX) {
|
||||
mSelected = static_cast<MenuID>(mSelected + 1);
|
||||
}
|
||||
} else if (input.trigger & VPAD_BUTTON_UP) {
|
||||
if (mSelected > MENU_ID_MIN) {
|
||||
mSelected = static_cast<MenuID>(mSelected - 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (input.trigger & VPAD_BUTTON_A) {
|
||||
switch (mSelected) {
|
||||
case MENU_ID_INFO:
|
||||
mSubscreen = std::make_unique<InfoScreen>();
|
||||
break;
|
||||
case MENU_ID_FLASH:
|
||||
mSubscreen = std::make_unique<FlashScreen>();
|
||||
break;
|
||||
case MENU_ID_SET_REGION:
|
||||
mSubscreen = std::make_unique<SetRegionScreen>();
|
||||
break;
|
||||
case MENU_ID_ABOUT:
|
||||
mSubscreen = std::make_unique<AboutScreen>();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
36
source/screens/MenuScreen.hpp
Normal file
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> mSubscreen;
|
||||
|
||||
enum MenuID {
|
||||
MENU_ID_INFO,
|
||||
MENU_ID_FLASH,
|
||||
MENU_ID_SET_REGION,
|
||||
MENU_ID_ABOUT,
|
||||
|
||||
MENU_ID_MIN = MENU_ID_INFO,
|
||||
MENU_ID_MAX = MENU_ID_ABOUT,
|
||||
};
|
||||
|
||||
struct MenuEntry {
|
||||
uint16_t icon;
|
||||
const char* name;
|
||||
};
|
||||
std::map<MenuID, MenuEntry> mEntries;
|
||||
MenuID mSelected = MENU_ID_MIN;
|
||||
};
|
144
source/screens/SetRegionScreen.cpp
Normal file
144
source/screens/SetRegionScreen.cpp
Normal file
@ -0,0 +1,144 @@
|
||||
#include "SetRegionScreen.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "Gfx.hpp"
|
||||
|
||||
#include <nsysccr/cdc.h>
|
||||
#include <nsysccr/cfg.h>
|
||||
|
||||
namespace {
|
||||
|
||||
bool SetRegionByte(uint8_t byte)
|
||||
{
|
||||
CCRCDCUicConfig cfg{};
|
||||
// custom config id which was added by the gamepad cfw
|
||||
cfg.configId = 1;
|
||||
// region byte + crc16
|
||||
cfg.size = 3;
|
||||
cfg.data[0] = byte;
|
||||
uint16_t crc = CCRCDCCalcCRC16(cfg.data, 1);
|
||||
cfg.data[1] = crc & 0xff;
|
||||
cfg.data[2] = (crc >> 8) & 0xff;
|
||||
if (CCRCDCPerSetUicConfig(CCR_CDC_DESTINATION_DRC0, &cfg) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Also update the cached eeprom
|
||||
return CCRCFGSetCachedEeprom(0, 0x103, cfg.data, cfg.size) == 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SetRegionScreen::SetRegionScreen()
|
||||
: mRegionEntries({
|
||||
{ REGION_JAPAN, "JAPAN" },
|
||||
{ REGION_AMERICA, "AMERICA" },
|
||||
{ REGION_EUROPE, "EUROPE" },
|
||||
{ REGION_CHINA, "CHINA" },
|
||||
{ REGION_SOUTH_KOREA, "SOUTH KOREA" },
|
||||
{ REGION_TAIWAN, "TAIWAN" },
|
||||
{ REGION_AUSTRALIA, "AUSTRALIA" },
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
SetRegionScreen::~SetRegionScreen()
|
||||
{
|
||||
}
|
||||
|
||||
void SetRegionScreen::Draw()
|
||||
{
|
||||
DrawTopBar("SetRegionScreen");
|
||||
|
||||
switch (mState)
|
||||
{
|
||||
case STATE_SELECT_REGION:
|
||||
for (Region id = REGION_JAPAN; id <= REGION_AUSTRALIA; id = static_cast<Region>(id + 1)) {
|
||||
int yOff = 75 + static_cast<int>(id) * 100;
|
||||
Gfx::DrawRectFilled(0, yOff, Gfx::SCREEN_WIDTH, 100, Gfx::COLOR_ALT_BACKGROUND);
|
||||
Gfx::Print(68, yOff + 100 / 2, 50, Gfx::COLOR_TEXT, mRegionEntries[id], Gfx::ALIGN_VERTICAL);
|
||||
|
||||
if (id == mRegion) {
|
||||
Gfx::DrawRect(0, yOff, Gfx::SCREEN_WIDTH, 100, 8, Gfx::COLOR_HIGHLIGHTED);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case STATE_CONFIRM:
|
||||
Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_TEXT,
|
||||
Utils::sprintf("Are you sure you want to set the region\nto %s?\n"
|
||||
"(Note that updating the region\nonly works with a modified firmware)",
|
||||
mRegionEntries[mRegion].c_str()), Gfx::ALIGN_CENTER);
|
||||
break;
|
||||
case STATE_UPDATE:
|
||||
Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_TEXT, "Updating region...", Gfx::ALIGN_CENTER);
|
||||
break;
|
||||
case STATE_DONE:
|
||||
Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_TEXT, "Done! Region has been changed.", Gfx::ALIGN_CENTER);
|
||||
break;
|
||||
case STATE_ERROR:
|
||||
Gfx::Print(Gfx::SCREEN_WIDTH / 2, Gfx::SCREEN_HEIGHT / 2, 64, Gfx::COLOR_ERROR, "Error! Failed to set region.\n(Is the firmware modified properly?)", Gfx::ALIGN_CENTER);
|
||||
break;
|
||||
}
|
||||
|
||||
if (mState == STATE_SELECT_REGION) {
|
||||
DrawBottomBar("\ue07d Navigate", "\ue044 Exit", "\ue000 Confirm / \ue001 Back");
|
||||
} else if (mState == STATE_CONFIRM) {
|
||||
DrawBottomBar(nullptr, "\ue044 Exit", "\ue000 Confirm / \ue001 Back");
|
||||
} else if (mState == STATE_UPDATE) {
|
||||
DrawBottomBar(nullptr, "Please wait...", nullptr);
|
||||
} else {
|
||||
DrawBottomBar(nullptr, "\ue044 Exit", "\ue001 Back");
|
||||
}
|
||||
}
|
||||
|
||||
bool SetRegionScreen::Update(VPADStatus& input)
|
||||
{
|
||||
switch (mState)
|
||||
{
|
||||
case STATE_SELECT_REGION:
|
||||
if (input.trigger & VPAD_BUTTON_B) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (input.trigger & VPAD_BUTTON_A) {
|
||||
mState = STATE_CONFIRM;
|
||||
break;
|
||||
}
|
||||
|
||||
if (input.trigger & VPAD_BUTTON_DOWN) {
|
||||
if (mRegion < REGION_AUSTRALIA) {
|
||||
mRegion = static_cast<Region>(mRegion + 1);
|
||||
}
|
||||
} else if (input.trigger & VPAD_BUTTON_UP) {
|
||||
if (mRegion > REGION_JAPAN) {
|
||||
mRegion = static_cast<Region>(mRegion - 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case STATE_CONFIRM:
|
||||
if (input.trigger & VPAD_BUTTON_B) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (input.trigger & VPAD_BUTTON_A) {
|
||||
mState = STATE_UPDATE;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case STATE_UPDATE:
|
||||
if (!SetRegionByte(static_cast<uint8_t>(mRegion))) {
|
||||
mState = STATE_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
mState = STATE_DONE;
|
||||
break;
|
||||
case STATE_DONE:
|
||||
case STATE_ERROR:
|
||||
if (input.trigger & VPAD_BUTTON_B) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
39
source/screens/SetRegionScreen.hpp
Normal file
39
source/screens/SetRegionScreen.hpp
Normal file
@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include "Screen.hpp"
|
||||
#include <map>
|
||||
#include <nsysccr/cdc.h>
|
||||
|
||||
class SetRegionScreen : public Screen
|
||||
{
|
||||
public:
|
||||
SetRegionScreen();
|
||||
virtual ~SetRegionScreen();
|
||||
|
||||
void Draw();
|
||||
|
||||
bool Update(VPADStatus& input);
|
||||
|
||||
private:
|
||||
enum State {
|
||||
STATE_SELECT_REGION,
|
||||
STATE_CONFIRM,
|
||||
STATE_UPDATE,
|
||||
STATE_DONE,
|
||||
STATE_ERROR,
|
||||
} mState = STATE_SELECT_REGION;
|
||||
|
||||
// From gamepad firmware @0x000b29fc
|
||||
enum Region {
|
||||
REGION_JAPAN = 0,
|
||||
REGION_AMERICA = 1,
|
||||
REGION_EUROPE = 2,
|
||||
REGION_CHINA = 3,
|
||||
REGION_SOUTH_KOREA = 4,
|
||||
REGION_TAIWAN = 5,
|
||||
REGION_AUSTRALIA = 6,
|
||||
} mRegion = REGION_JAPAN;
|
||||
std::map<Region, std::string> mRegionEntries;
|
||||
|
||||
std::string mErrorText;
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user