mirror of
https://github.com/GaryOderNichts/recovery_menu.git
synced 2025-06-18 18:55:31 -04:00
Initial commit
This commit is contained in:
commit
ad6fc39cd2
19
.github/workflows/build.yml
vendored
Normal file
19
.github/workflows/build.yml
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
name: Build
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build-binary:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Build recovery
|
||||
run: |
|
||||
docker build -t recoverybuilder .
|
||||
docker run --rm -v ${PWD}:/project recoverybuilder make
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: recovery_menu
|
||||
path: recovery_menu
|
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
.vscode/
|
||||
build/
|
||||
|
||||
*.elf
|
||||
*.bin
|
||||
*.bin.h
|
||||
*_syms.h
|
||||
|
||||
/recovery_menu
|
5
Dockerfile
Normal file
5
Dockerfile
Normal file
@ -0,0 +1,5 @@
|
||||
FROM devkitpro/devkitarm:20220531
|
||||
|
||||
ENV PATH=$DEVKITARM/bin:$PATH
|
||||
|
||||
WORKDIR /project
|
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.
|
22
Makefile
Normal file
22
Makefile
Normal file
@ -0,0 +1,22 @@
|
||||
#-------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
.PHONY: all clean ios_kernel ios_mcp
|
||||
|
||||
all: recovery_menu
|
||||
@echo "\033[92mDone!\033[0m"
|
||||
|
||||
recovery_menu: ios_kernel
|
||||
python3 build_recovery_file.py $@
|
||||
|
||||
ios_mcp:
|
||||
@$(MAKE) --no-print-directory -C $(CURDIR)/ios_mcp
|
||||
|
||||
ios_kernel: ios_mcp
|
||||
@$(MAKE) --no-print-directory -C $(CURDIR)/ios_kernel
|
||||
|
||||
clean:
|
||||
@$(MAKE) --no-print-directory -C $(CURDIR)/ios_kernel clean
|
||||
@$(MAKE) --no-print-directory -C $(CURDIR)/ios_mcp clean
|
||||
rm -f recovery_menu
|
60
README.md
Normal file
60
README.md
Normal file
@ -0,0 +1,60 @@
|
||||

|
||||
|
||||
# Wii U Recovery Menu
|
||||
A simple recovery menu running on the IOSU for unbricking, which can be booted using [udpih](https://github.com/GaryOderNichts/udpih).
|
||||
|
||||
## Options
|
||||
### Set Coldboot Title
|
||||
Allows changing the current title the console boots to.
|
||||
Useful for unbricking CBHC bricks.
|
||||
Possible options are:
|
||||
- `Wii U Menu (JPN) - 00050010-10040000`
|
||||
- `Wii U Menu (USA) - 00050010-10040100`
|
||||
- `Wii U Menu (EUR) - 00050010-10040200`
|
||||
|
||||
### Dump Syslogs
|
||||
Copies all system logs to a `logs` folder on the root of the SD Card.
|
||||
|
||||
### Dump OTP + SEEPROM
|
||||
Dumps the OTP and SEEPROM to `otp.bin` and `seeprom.bin` on the root of the SD Card.
|
||||
|
||||
### Start wupserver
|
||||
Starts wupserver which allows connecting to the console from a PC using [wupclient](https://gist.github.com/GaryOderNichts/409672b1bd5627b9dc506fe0f812ec9e).
|
||||
|
||||
### Load Network Configuration
|
||||
Loads a network configuration from the SD, and temporarily applies it to use wupserver.
|
||||
The configurations will be loaded from a `network.cfg` file on the root of your SD.
|
||||
For using the ethernet adapter, the file should look like this:
|
||||
```
|
||||
type=eth
|
||||
```
|
||||
For using wifi:
|
||||
```
|
||||
type=wifi
|
||||
ssid=ssidhere
|
||||
key=wifikeyhere
|
||||
key_type=WPA2_PSK_AES
|
||||
```
|
||||
|
||||
### Displays DRC Pin
|
||||
Displays the Gamepad Pin used for pairing the gamepad.
|
||||
The numeric values represent the following symbols: `♠ = 0, ♥ = 1, ♦ = 2, ♣ = 3`.
|
||||
|
||||
### Install WUP
|
||||
Installs a valid signed WUP from the `install` folder on the root of your SD Card.
|
||||
Don't place the WUP into any subfolders.
|
||||
|
||||
## Building
|
||||
```bash
|
||||
# build the docker container
|
||||
docker build -t recoverybuilder .
|
||||
|
||||
# build the menu
|
||||
docker run -it --rm -v ${PWD}:/project recoverybuilder make
|
||||
```
|
||||
|
||||
## Credits
|
||||
- [@Maschell](https://github.com/Maschell) for the [network configuration types](https://github.com/devkitPro/wut/commit/159f578b34401cd4365efd7b54b536154c9dc576)
|
||||
- [@dimok789](https://github.com/dimok789) for [mocha](https://github.com/dimok789/mocha)
|
||||
- [@hexkyz](https://github.com/hexkyz) for [hexFW](https://github.com/hexkyz/hexFW)
|
||||
- [@rw-r-r-0644](https://github.com/rw-r-r-0644) for the lolserial code
|
51
build_recovery_file.py
Normal file
51
build_recovery_file.py
Normal file
@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import sys, struct
|
||||
|
||||
RECOVERY_HEADER_SIZE = 0xec
|
||||
|
||||
def defSection(vaddr, paddr, size, offset):
|
||||
return struct.pack('>IIII', vaddr, paddr, size, offset)
|
||||
|
||||
def main(argc, argv):
|
||||
if argc != 2:
|
||||
sys.exit(-1)
|
||||
|
||||
# magic
|
||||
data = b'REC\0'
|
||||
|
||||
# entrypoint and numSections
|
||||
data += struct.pack('>II', 0x08136000, 2)
|
||||
|
||||
# read kernel binary
|
||||
kernel_bin = b''
|
||||
with open("ios_kernel/ios_kernel.bin", "rb") as f:
|
||||
kernel_bin += f.read()
|
||||
|
||||
# kernel section
|
||||
data += defSection(0x08136000, 0x08136000, len(kernel_bin), RECOVERY_HEADER_SIZE)
|
||||
|
||||
# read mcp binary
|
||||
mcp_bin = b''
|
||||
with open("ios_mcp/ios_mcp.bin", "rb") as f:
|
||||
mcp_bin += f.read()
|
||||
|
||||
# mcp section
|
||||
data += defSection(0x05116000, 0x05116000 - 0x05100000 + 0x13d80000, len(mcp_bin), RECOVERY_HEADER_SIZE + len(kernel_bin))
|
||||
|
||||
# pad the rest of the section structs
|
||||
for i in range(12):
|
||||
data += defSection(0, 0, 0, 0)
|
||||
|
||||
# append kernel binary
|
||||
data += kernel_bin
|
||||
|
||||
# append mcp binary
|
||||
data += mcp_bin
|
||||
|
||||
# write file
|
||||
with open(argv[1], 'wb') as f:
|
||||
f.write(data)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(len(sys.argv), sys.argv)
|
135
ios_kernel/Makefile
Normal file
135
ios_kernel/Makefile
Normal file
@ -0,0 +1,135 @@
|
||||
#---------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
ifeq ($(strip $(DEVKITPRO)),)
|
||||
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
|
||||
endif
|
||||
|
||||
TOPDIR ?= $(CURDIR)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# iosu_rules
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(DEVKITARM)),)
|
||||
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>/devkitARM")
|
||||
endif
|
||||
|
||||
include $(DEVKITARM)/base_rules
|
||||
export OBJDUMP := $(PREFIX)objdump
|
||||
|
||||
MACHDEP = -DSTARBUCK -mbig-endian -mcpu=arm926ej-s -msoft-float -mfloat-abi=soft
|
||||
|
||||
%.elf:
|
||||
@echo linking ... $(notdir $@)
|
||||
$(SILENTCMD)$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# TARGET is the name of the output
|
||||
# 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
|
||||
#---------------------------------------------------------------------------------
|
||||
TARGET := $(notdir $(CURDIR))
|
||||
BUILD := build
|
||||
SOURCES := source
|
||||
DATA := data
|
||||
INCLUDES := source
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#---------------------------------------------------------------------------------
|
||||
CFLAGS := -Wall -std=gnu11 -Os -flto $(MACHDEP) $(INCLUDE) -Wno-stringop-overflow
|
||||
|
||||
ASFLAGS := $(MACHDEP)
|
||||
|
||||
LDFLAGS := -nostartfiles -nodefaultlibs -mbig-endian -flto \
|
||||
-Wl,-L $(TOPDIR) -Wl,-Map,$(notdir $*.map),-T $(TOPDIR)/link.ld
|
||||
|
||||
LIBS := -lgcc
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level
|
||||
# containing include and lib
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBDIRS :=
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# 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 TARGETNAME := $(TARGET)
|
||||
|
||||
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)))
|
||||
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||
|
||||
export LD := $(CC)
|
||||
|
||||
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
|
||||
export OFILES_SRC := $(SFILES:.s=.o) $(CFILES:.c=.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)
|
||||
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
|
||||
.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).elf $(TARGET).bin
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
|
||||
DEPENDS := $(OFILES:.o=.d)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
all : $(OUTPUT).bin
|
||||
|
||||
$(OUTPUT).elf : $(OFILES)
|
||||
|
||||
$(OUTPUT).bin: $(OUTPUT).elf
|
||||
@echo "built ... $(notdir $@)"
|
||||
@$(OBJCOPY) -j .text -j .rodata -j .data -O binary $(OUTPUT).elf $@
|
||||
|
||||
$(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)
|
||||
|
||||
-include $(DEPENDS)
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------------
|
32
ios_kernel/imports.ld
Normal file
32
ios_kernel/imports.ld
Normal file
@ -0,0 +1,32 @@
|
||||
PROVIDE(memcpy = 0x08131d04);
|
||||
PROVIDE(memset = 0x08131da0);
|
||||
PROVIDE(strncpy = 0x081329b8);
|
||||
PROVIDE(vsnprintf = 0x0813293c);
|
||||
PROVIDE(snprintf = 0x08132988);
|
||||
|
||||
PROVIDE(disable_interrupts = 0x0812e778);
|
||||
PROVIDE(enable_interrupts = 0x0812e78c);
|
||||
|
||||
PROVIDE(invalidate_icache = 0x0812dcf0);
|
||||
PROVIDE(invalidate_dcache = 0x08120164);
|
||||
PROVIDE(flush_dcache = 0x08120160);
|
||||
PROVIDE(setClientCapabilities = 0x081260a8);
|
||||
PROVIDE(_iosMapSharedUserExecution = 0x08124f88);
|
||||
PROVIDE(readOTP = 0x08120248);
|
||||
|
||||
PROVIDE(IOS_HeapAlloc = 0x081234e4);
|
||||
PROVIDE(IOS_HeapAllocAligned = 0x08123464);
|
||||
PROVIDE(IOS_HeapFree = 0x08123830);
|
||||
|
||||
PROVIDE(IOS_Open = 0x0812940c);
|
||||
PROVIDE(IOS_Close = 0x08129368);
|
||||
PROVIDE(IOS_Ioctl = 0x081290e0);
|
||||
PROVIDE(IOS_Ioctlv = 0x0812903c);
|
||||
|
||||
PROVIDE(IOS_VirtToPhys = 0x08124484);
|
||||
|
||||
PROVIDE(IOS_Shutdown = 0xffffdc48);
|
||||
PROVIDE(IOS_Reset = 0x08129760);
|
||||
|
||||
PROVIDE(currentThreadContext = 0x08173ba0);
|
||||
PROVIDE(domainAccessPermissions = 0x081a4000);
|
29
ios_kernel/link.ld
Normal file
29
ios_kernel/link.ld
Normal file
@ -0,0 +1,29 @@
|
||||
OUTPUT_ARCH(arm)
|
||||
|
||||
INCLUDE "imports.ld"
|
||||
|
||||
SECTIONS {
|
||||
. = (0x08136000);
|
||||
|
||||
.text : {
|
||||
crt0.o(.init)
|
||||
*(.text*);
|
||||
}
|
||||
.rodata : {
|
||||
*(.rodata*)
|
||||
}
|
||||
.data : {
|
||||
*(.data*)
|
||||
}
|
||||
.bss : {
|
||||
__kernel_bss_start = .;
|
||||
*(.bss*)
|
||||
}
|
||||
__kernel_bss_end = .;
|
||||
|
||||
/DISCARD/ : {
|
||||
*(*);
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT((SIZEOF(.text) + SIZEOF(.rodata) + SIZEOF(.data) + SIZEOF(.bss)) < 0xa000, "ios_kernel is too big");
|
9
ios_kernel/source/crt0.s
Normal file
9
ios_kernel/source/crt0.s
Normal file
@ -0,0 +1,9 @@
|
||||
.section ".init"
|
||||
.arm
|
||||
.align 4
|
||||
|
||||
.extern _main
|
||||
.type _main, %function
|
||||
|
||||
_start:
|
||||
b _main
|
69
ios_kernel/source/imports.h
Normal file
69
ios_kernel/source/imports.h
Normal file
@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
#define ARM_B(addr, func) (0xEA000000 | ((((uint32_t)(func) - (uint32_t)(addr) - 8) >> 2) & 0x00FFFFFF)) // +-32MB
|
||||
#define ARM_BL(addr, func) (0xEB000000 | ((((uint32_t)(func) - (uint32_t)(addr) - 8) >> 2) & 0x00FFFFFF)) // +-32MB
|
||||
#define THUMB_B(addr, func) ((0xE000 | ((((uint32_t)(func) - (uint32_t)(addr) - 4) >> 1) & 0x7FF))) // +-2KB
|
||||
#define THUMB_BL(addr, func) ((0xF000F800 | ((((uint32_t)(func) - (uint32_t)(addr) - 4) >> 1) & 0x0FFF)) | ((((uint32_t)(func) - (uint32_t)(addr) - 4) << 4) & 0x7FFF000)) // +-4MB
|
||||
|
||||
typedef struct {
|
||||
void* ptr;
|
||||
uint32_t len;
|
||||
uint32_t paddr;
|
||||
} IOSVec_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t paddr;
|
||||
uint32_t vaddr;
|
||||
uint32_t size;
|
||||
uint32_t domain;
|
||||
// 0 = undefined, 1 = kernel only, 2 = read only, 3 = read write
|
||||
uint32_t type;
|
||||
uint32_t cached;
|
||||
} ios_map_shared_info_t;
|
||||
|
||||
int disable_interrupts(void);
|
||||
int enable_interrupts(int);
|
||||
|
||||
void invalidate_icache(void);
|
||||
void invalidate_dcache(void* ptr, uint32_t size);
|
||||
void flush_dcache(void* ptr, uint32_t size);
|
||||
int setClientCapabilities(int processId, int featureId, uint64_t featureMask);
|
||||
int _iosMapSharedUserExecution(ios_map_shared_info_t* info);
|
||||
int readOTP(int index, void* buf, uint32_t size);
|
||||
|
||||
void* IOS_HeapAlloc(uint32_t heap, uint32_t size);
|
||||
void* IOS_HeapAllocAligned(uint32_t heap, uint32_t size, uint32_t alignment);
|
||||
void IOS_HeapFree(uint32_t heap, void* ptr);
|
||||
|
||||
int IOS_Open(const char* device, int mode);
|
||||
int IOS_Close(int fd);
|
||||
int IOS_Ioctl(int fd, uint32_t request, void* input_buffer, uint32_t input_buffer_len, void* output_buffer, uint32_t output_buffer_len);
|
||||
int IOS_Ioctlv(int fd, uint32_t request, uint32_t vector_count_in, uint32_t vector_count_out, IOSVec_t* vector);
|
||||
|
||||
void* IOS_VirtToPhys(void* ptr);
|
||||
|
||||
void IOS_Shutdown(int reset);
|
||||
void IOS_Reset(void);
|
||||
|
||||
static inline uint32_t disable_mmu(void)
|
||||
{
|
||||
uint32_t control_register = 0;
|
||||
asm volatile("MRC p15, 0, %0, c1, c0, 0" : "=r" (control_register));
|
||||
asm volatile("MCR p15, 0, %0, c1, c0, 0" : : "r" (control_register & 0xFFFFEFFA));
|
||||
return control_register;
|
||||
}
|
||||
|
||||
static inline void restore_mmu(uint32_t control_register)
|
||||
{
|
||||
asm volatile("MCR p15, 0, %0, c1, c0, 0" : : "r" (control_register));
|
||||
}
|
||||
|
||||
static inline void set_domain_register(uint32_t domain_register)
|
||||
{
|
||||
asm volatile("MCR p15, 0, %0, c3, c0, 0" : : "r"(domain_register));
|
||||
}
|
64
ios_kernel/source/lolserial.c
Normal file
64
ios_kernel/source/lolserial.c
Normal file
@ -0,0 +1,64 @@
|
||||
#include "lolserial.h"
|
||||
#include "imports.h"
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define LT_REG_BASE (0x0d800000)
|
||||
#define LT_TIMER (LT_REG_BASE + 0x010)
|
||||
#define LT_GPIO_ENABLE (LT_REG_BASE + 0x0dc)
|
||||
#define LT_GPIO_OUT (LT_REG_BASE + 0x0e0)
|
||||
#define LT_GPIO_DIR (LT_REG_BASE + 0x0e4)
|
||||
#define LT_GPIO_OWNER (LT_REG_BASE + 0x0fc)
|
||||
|
||||
#define LOLSERIAL_WAIT_TICKS 200
|
||||
|
||||
#if 0
|
||||
#define LOLSERIAL_PIN 0x00000100 //GP_SENSORBAR
|
||||
#else
|
||||
#define LOLSERIAL_PIN 0x00010000 // GP_DEBUG0
|
||||
#endif
|
||||
|
||||
void lolserial_lprint(const char *str, int len)
|
||||
{
|
||||
/* setup output pin */
|
||||
*(volatile uint32_t*) LT_GPIO_OWNER &= ~LOLSERIAL_PIN;
|
||||
*(volatile uint32_t*) LT_GPIO_ENABLE |= LOLSERIAL_PIN;
|
||||
*(volatile uint32_t*) LT_GPIO_DIR |= LOLSERIAL_PIN;
|
||||
*(volatile uint32_t*) LT_GPIO_OUT |= LOLSERIAL_PIN;
|
||||
|
||||
/* loop until null terminator or string end */
|
||||
for (const char *end = str + len; *str && (str != end); str++) {
|
||||
for (uint32_t bits = 0x200 | (*str << 1); bits; bits >>= 1) {
|
||||
/* set bit value */
|
||||
*(volatile uint32_t*) LT_GPIO_OUT = ((*(volatile uint32_t*) LT_GPIO_OUT) & ~LOLSERIAL_PIN) | ((bits & 1) ? LOLSERIAL_PIN : 0);
|
||||
|
||||
/* wait ticks for bit */
|
||||
uint32_t now = *(volatile uint32_t*) LT_TIMER, then = now + LOLSERIAL_WAIT_TICKS;
|
||||
for (; then < now; now = *(volatile uint32_t*) LT_TIMER); /* wait overflow */
|
||||
for (; now < then; now = *(volatile uint32_t*) LT_TIMER); /* wait */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void lolserial_print(const char *str)
|
||||
{
|
||||
lolserial_lprint(str, -1);
|
||||
}
|
||||
|
||||
void lolserial_printf(const char* format, ...)
|
||||
{
|
||||
int level = disable_interrupts();
|
||||
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
|
||||
char buffer[0x100];
|
||||
|
||||
int len = vsnprintf(buffer, sizeof(buffer), format, args);
|
||||
lolserial_lprint(buffer, len);
|
||||
|
||||
va_end(args);
|
||||
|
||||
enable_interrupts(level);
|
||||
}
|
7
ios_kernel/source/lolserial.h
Normal file
7
ios_kernel/source/lolserial.h
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
void lolserial_lprint(const char *str, int len);
|
||||
|
||||
void lolserial_print(const char *str);
|
||||
|
||||
void lolserial_printf(const char* fmt, ...);
|
94
ios_kernel/source/main.c
Normal file
94
ios_kernel/source/main.c
Normal file
@ -0,0 +1,94 @@
|
||||
#include "imports.h"
|
||||
#include "thread.h"
|
||||
#include "lolserial.h"
|
||||
#include "../../ios_mcp/ios_mcp_syms.h"
|
||||
|
||||
extern char __kernel_bss_start;
|
||||
extern char __kernel_bss_end;
|
||||
|
||||
extern char svcAB_handler;
|
||||
|
||||
extern uint32_t domainAccessPermissions[];
|
||||
|
||||
int kernel_syscall_0x81(int type, uint32_t address, uint32_t value)
|
||||
{
|
||||
int res = 0;
|
||||
int level = disable_interrupts();
|
||||
set_domain_register(domainAccessPermissions[0]); // 0 = KERNEL
|
||||
|
||||
if (type == 0) { // kernRead32
|
||||
res = *(volatile uint32_t*) address;
|
||||
} else if (type == 1) { // kernWrite32
|
||||
*(volatile uint32_t*) address = value;
|
||||
} else if (type == 2) { // readOtp
|
||||
res = readOTP(0, (void*) address, value);
|
||||
}
|
||||
|
||||
set_domain_register(domainAccessPermissions[currentThreadContext->pid]);
|
||||
enable_interrupts(level);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int _main(void* arg)
|
||||
{
|
||||
lolserial_printf("Hello world from recovery_menu. Running from '%s'.\n", arg);
|
||||
|
||||
int level = disable_interrupts();
|
||||
uint32_t control_register = disable_mmu();
|
||||
|
||||
// clear all bss
|
||||
memset(&__kernel_bss_start, 0, &__kernel_bss_end - &__kernel_bss_start);
|
||||
memset((void*) (__mcp_bss_start - 0x05074000 + 0x08234000), 0, __mcp_bss_end - __mcp_bss_start);
|
||||
|
||||
// map the mcp sections
|
||||
ios_map_shared_info_t map_info;
|
||||
map_info.paddr = 0x050bd000 - 0x05000000 + 0x081c0000;
|
||||
map_info.vaddr = 0x050bd000;
|
||||
map_info.size = 0x3000;
|
||||
map_info.domain = 1; // MCP
|
||||
map_info.type = 3;
|
||||
map_info.cached = 0xffffffff;
|
||||
_iosMapSharedUserExecution(&map_info);
|
||||
|
||||
map_info.paddr = 0x05116000 - 0x05100000 + 0x13d80000;
|
||||
map_info.vaddr = 0x05116000;
|
||||
map_info.size = 0x4000;
|
||||
map_info.domain = 1; // MCP
|
||||
map_info.type = 3;
|
||||
map_info.cached = 0xffffffff;
|
||||
_iosMapSharedUserExecution(&map_info);
|
||||
|
||||
// redirect __sys_write0 to lolserial
|
||||
*(volatile uint32_t*) 0x0812dd68 = ARM_B(0x0812dd68, (uint32_t) &svcAB_handler);
|
||||
|
||||
// add mcp ioctl hook to start mcp thread
|
||||
*(volatile uint32_t*) (0x05025242 - 0x05000000 + 0x081c0000) = THUMB_BL(0x05025242, _MCP_ioctl100_patch);
|
||||
|
||||
// replace custom kernel syscall
|
||||
*(volatile uint32_t*) 0x0812cd2c = ARM_B(0x0812cd2c, kernel_syscall_0x81);
|
||||
|
||||
restore_mmu(control_register);
|
||||
|
||||
// invalidate all cache
|
||||
invalidate_dcache(NULL, 0x4001);
|
||||
invalidate_icache();
|
||||
|
||||
enable_interrupts(level);
|
||||
|
||||
// give the current thread full access to MCP for starting the thread
|
||||
setClientCapabilities(currentThreadContext->pid, 0xd, 0xffffffffffffffffllu);
|
||||
|
||||
// start mcp thread
|
||||
int mcpHandle = IOS_Open("/dev/mcp", 0);
|
||||
if (mcpHandle > 0) {
|
||||
lolserial_printf("Starting MCP thread...\n");
|
||||
IOS_Ioctl(mcpHandle, 100, NULL, 0, NULL, 0);
|
||||
|
||||
IOS_Close(mcpHandle);
|
||||
} else {
|
||||
lolserial_printf("Cannot open MCP: %x\n", mcpHandle);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
11
ios_kernel/source/svcAB_handler.s
Normal file
11
ios_kernel/source/svcAB_handler.s
Normal file
@ -0,0 +1,11 @@
|
||||
.arm
|
||||
|
||||
.extern lolserial_print
|
||||
|
||||
.global svcAB_handler
|
||||
svcAB_handler:
|
||||
ldmia sp, {r0, r1}
|
||||
cmp r0, #0x4
|
||||
mov r0, r1
|
||||
bleq lolserial_print
|
||||
ldmia sp!, {r0-r12, pc}^
|
33
ios_kernel/source/thread.h
Normal file
33
ios_kernel/source/thread.h
Normal file
@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct ThreadContext {
|
||||
uint32_t cspr;
|
||||
uint32_t gpr[14];
|
||||
uint32_t lr;
|
||||
uint32_t pc;
|
||||
struct ThreadContext* threadQueueNext;
|
||||
uint32_t maxPriority;
|
||||
uint32_t priority;
|
||||
uint32_t state;
|
||||
uint32_t pid;
|
||||
uint32_t id;
|
||||
uint32_t flags;
|
||||
uint32_t exitValue;
|
||||
struct ThreadContext** joinQueue;
|
||||
struct ThreadContext** threadQueue;
|
||||
uint8_t unk1[0x38];
|
||||
void* stackPointer;
|
||||
uint8_t unk2[8];
|
||||
void* sysStackAddr;
|
||||
void* userStackAddr;
|
||||
uint32_t userStackSize;
|
||||
void* threadLocalStorage;
|
||||
uint32_t profileCount;
|
||||
uint32_t profileTime;
|
||||
} ThreadContext_t;
|
||||
static_assert(sizeof(ThreadContext_t) == 0xc8, "ThreadContext_t: different size than expected");
|
||||
|
||||
extern ThreadContext_t* currentThreadContext;
|
142
ios_mcp/Makefile
Normal file
142
ios_mcp/Makefile
Normal file
@ -0,0 +1,142 @@
|
||||
#---------------------------------------------------------------------------------
|
||||
.SUFFIXES:
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
ifeq ($(strip $(DEVKITPRO)),)
|
||||
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>/devkitpro")
|
||||
endif
|
||||
|
||||
TOPDIR ?= $(CURDIR)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# iosu_rules
|
||||
#---------------------------------------------------------------------------------
|
||||
ifeq ($(strip $(DEVKITARM)),)
|
||||
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>/devkitARM")
|
||||
endif
|
||||
|
||||
include $(DEVKITARM)/base_rules
|
||||
export OBJDUMP := $(PREFIX)objdump
|
||||
|
||||
MACHDEP = -DSTARBUCK -mbig-endian -mcpu=arm926ej-s -msoft-float -mfloat-abi=soft
|
||||
|
||||
%.elf:
|
||||
@echo linking ... $(notdir $@)
|
||||
$(SILENTCMD)$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
|
||||
#---------------------------------------------------------------------------------
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# TARGET is the name of the output
|
||||
# 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
|
||||
#---------------------------------------------------------------------------------
|
||||
TARGET := $(notdir $(CURDIR))
|
||||
BUILD := build
|
||||
SOURCES := source
|
||||
DATA := data
|
||||
INCLUDES := source
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# options for code generation
|
||||
#---------------------------------------------------------------------------------
|
||||
CFLAGS := -Wall -std=gnu11 -Os -flto -fno-tree-loop-distribute-patterns -fno-builtin $(MACHDEP) $(INCLUDE)
|
||||
|
||||
ASFLAGS := $(MACHDEP)
|
||||
|
||||
LDFLAGS := -nostartfiles -nodefaultlibs -mbig-endian -flto \
|
||||
-Wl,-L $(TOPDIR) -Wl,-Map,$(notdir $*.map),-T $(TOPDIR)/link.ld
|
||||
|
||||
LIBS := -lgcc
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# list of directories containing libraries, this must be the top level
|
||||
# containing include and lib
|
||||
#---------------------------------------------------------------------------------
|
||||
LIBDIRS :=
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# 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 TARGETNAME := $(TARGET)
|
||||
|
||||
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)))
|
||||
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
||||
|
||||
export LD := $(CC)
|
||||
|
||||
export OFILES_BIN := $(addsuffix .o,$(BINFILES))
|
||||
export OFILES_SRC := $(SFILES:.s=.o) $(CFILES:.c=.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)
|
||||
|
||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
|
||||
.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).elf $(TARGET).bin $(TARGET)_syms.h
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
else
|
||||
|
||||
DEPENDS := $(OFILES:.o=.d)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# main targets
|
||||
#---------------------------------------------------------------------------------
|
||||
all : $(OUTPUT).bin $(OUTPUT)_syms.h
|
||||
|
||||
$(OUTPUT).elf : $(OFILES)
|
||||
|
||||
$(OUTPUT).bin: $(OUTPUT).elf
|
||||
@echo "built ... $(notdir $@)"
|
||||
@$(OBJCOPY) -j .text -j .rodata -j .data -O binary $(OUTPUT).elf $@
|
||||
|
||||
$(OUTPUT)_syms.h:
|
||||
@echo "#ifndef $(TARGETNAME)_SYMS_H" > $@
|
||||
@echo "#define $(TARGETNAME)_SYMS_H" >> $@
|
||||
@$(OBJDUMP) -EB -t -marm $(OUTPUT).elf | grep 'g F .text' | grep -v '.hidden' | awk '{print "#define " $$6 " 0x" $$1}' >> $@
|
||||
@$(OBJDUMP) -EB -t -marm $(OUTPUT).elf | grep -e 'g .text' -e '_bss_' | awk '{print "#define " $$5 " 0x" $$1}' >> $@
|
||||
@echo "#endif" >> $@
|
||||
|
||||
$(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)
|
||||
|
||||
-include $(DEPENDS)
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
endif
|
||||
#---------------------------------------------------------------------------------------
|
44
ios_mcp/imports.ld
Normal file
44
ios_mcp/imports.ld
Normal file
@ -0,0 +1,44 @@
|
||||
/* PROVIDE(printf = 0x0503dcc0); */
|
||||
PROVIDE(printf = 0x05055438);
|
||||
PROVIDE(memcmp = 0x05054d6c);
|
||||
PROVIDE(memcpy = 0x05054e54);
|
||||
PROVIDE(memset = 0x05054ef0);
|
||||
PROVIDE(vsnprintf = 0x05055c40);
|
||||
PROVIDE(snprintf = 0x05055c8c);
|
||||
PROVIDE(strncpy = 0x05055db4);
|
||||
PROVIDE(strnlen = 0x05055e94);
|
||||
PROVIDE(strncmp = 0x05055e10);
|
||||
PROVIDE(usleep = 0x050564e4);
|
||||
|
||||
PROVIDE(bspWrite = 0x0503d460);
|
||||
PROVIDE(bspRead = 0x0503d550);
|
||||
|
||||
PROVIDE(IOS_CreateThread = 0x050567ec);
|
||||
PROVIDE(IOS_JoinThread = 0x050567f4);
|
||||
PROVIDE(IOS_CancelThread = 0x050567fc);
|
||||
PROVIDE(IOS_StartThread = 0x05056824);
|
||||
PROVIDE(IOS_GetThreadPriority = 0x0505683c);
|
||||
|
||||
PROVIDE(IOS_CreateMessageQueue = 0x0505684c);
|
||||
PROVIDE(IOS_DestroyMessageQueue = 0x05056854);
|
||||
PROVIDE(IOS_ReceiveMessage = 0x0505686c);
|
||||
|
||||
PROVIDE(IOS_HeapAlloc = 0x05056924);
|
||||
PROVIDE(IOS_HeapAllocAligned = 0x0505692c);
|
||||
PROVIDE(IOS_HeapFree = 0x05056934);
|
||||
|
||||
PROVIDE(IOS_Open = 0x05056984);
|
||||
PROVIDE(IOS_Close = 0x0505698c);
|
||||
PROVIDE(IOS_Ioctl = 0x050569ac);
|
||||
PROVIDE(IOS_Ioctlv = 0x050569b4);
|
||||
PROVIDE(IOS_IoctlvAsync = 0x050569ec);
|
||||
|
||||
PROVIDE(IOS_InvalidateDCache = 0x05056a74);
|
||||
PROVIDE(IOS_FlushDCache = 0x05056a7c);
|
||||
|
||||
PROVIDE(IOS_Shutdown = 0x05056b7c);
|
||||
PROVIDE(IOS_Syscall0x81 = 0x05056bf4);
|
||||
|
||||
PROVIDE(ppcHeartBeatThreadId = 0x05070458);
|
||||
PROVIDE(currentColdbootOS = 0x050b8174);
|
||||
PROVIDE(currentColdbootTitle = 0x050b817c);
|
24
ios_mcp/link.ld
Normal file
24
ios_mcp/link.ld
Normal file
@ -0,0 +1,24 @@
|
||||
OUTPUT_ARCH(arm)
|
||||
|
||||
INCLUDE "imports.ld"
|
||||
|
||||
SECTIONS {
|
||||
.text 0x05116000 : {
|
||||
*(.text*);
|
||||
*(.rodata*);
|
||||
*(.data*);
|
||||
}
|
||||
|
||||
.bss 0x050bd000 : {
|
||||
__mcp_bss_start = .;
|
||||
*(.bss*);
|
||||
}
|
||||
__mcp_bss_end = .;
|
||||
|
||||
/DISCARD/ : {
|
||||
*(*);
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT(SIZEOF(.text) < 0x4000, "ios_mcp text is too big");
|
||||
ASSERT(SIZEOF(.bss) < 0x3000, "ios_mcp bss is too big");
|
54
ios_mcp/source/ccr.c
Normal file
54
ios_mcp/source/ccr.c
Normal file
@ -0,0 +1,54 @@
|
||||
#include "ccr.h"
|
||||
#include "imports.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static void* allocIobuf(uint32_t size)
|
||||
{
|
||||
void* ptr = IOS_HeapAlloc(0xcaff, size);
|
||||
|
||||
memset(ptr, 0x00, size);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void freeIobuf(void* ptr)
|
||||
{
|
||||
IOS_HeapFree(0xcaff, ptr);
|
||||
}
|
||||
|
||||
int CCRCDCGetMacAddress(int handle, uint8_t destId, void* macAddress)
|
||||
{
|
||||
uint8_t* buf = allocIobuf(0x128 + sizeof(IOSVec_t) * 2);
|
||||
IOSVec_t* vecs = (IOSVec_t*) (buf + 0x128);
|
||||
|
||||
buf[0x17] = destId;
|
||||
vecs[0].ptr = buf;
|
||||
vecs[0].len = 0x128;
|
||||
vecs[1].ptr = buf;
|
||||
vecs[1].len = 0x128;
|
||||
|
||||
int res = IOS_Ioctlv(handle, 0xc9, 1, 1, vecs);
|
||||
if (res >= 0) {
|
||||
memcpy(macAddress, buf + 0x18, 7);
|
||||
}
|
||||
|
||||
freeIobuf(buf);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int CCRSysGetPincode(int handle, uint8_t* pin)
|
||||
{
|
||||
uint8_t mac[7];
|
||||
int res = CCRCDCGetMacAddress(handle, 1, mac);
|
||||
if (res >= 0) {
|
||||
pin[3] = mac[6] & 3;
|
||||
pin[2] = (mac[6] >> 2) & 3;
|
||||
pin[1] = (mac[6] >> 4) & 3;
|
||||
pin[0] = mac[6] >> 6;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
7
ios_mcp/source/ccr.h
Normal file
7
ios_mcp/source/ccr.h
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
int CCRCDCGetMacAddress(int handle, uint8_t destId, void* macAddress);
|
||||
|
||||
int CCRSysGetPincode(int handle, uint8_t* pin);
|
65
ios_mcp/source/font_bin.h
Normal file
65
ios_mcp/source/font_bin.h
Normal file
@ -0,0 +1,65 @@
|
||||
static const unsigned char font_bin[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x18,
|
||||
0x18, 0x00, 0x0c, 0x00, 0x00, 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x66, 0x66, 0xff, 0x66, 0xff, 0x66, 0x66, 0x00, 0x18, 0x7c, 0x06,
|
||||
0x3c, 0x60, 0x3e, 0x18, 0x10, 0x46, 0x66, 0x30, 0x18, 0x0c, 0x66, 0x62,
|
||||
0x00, 0x3c, 0x66, 0x3c, 0x1c, 0xe6, 0x66, 0xfc, 0x00, 0x18, 0x0c, 0x06,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18, 0x0c, 0x0c, 0x18, 0x30, 0x00,
|
||||
0x00, 0x0c, 0x18, 0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x66, 0x3c, 0xff,
|
||||
0x3c, 0x66, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x3e,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00,
|
||||
0x00, 0x40, 0x60, 0x30, 0x18, 0x0c, 0x06, 0x00, 0x00, 0x3c, 0x66, 0x76,
|
||||
0x6e, 0x66, 0x3c, 0x00, 0x00, 0x18, 0x1c, 0x18, 0x18, 0x18, 0x7e, 0x00,
|
||||
0x00, 0x3c, 0x62, 0x30, 0x0c, 0x06, 0x7e, 0x00, 0x00, 0x3c, 0x62, 0x38,
|
||||
0x60, 0x66, 0x3c, 0x00, 0x00, 0x6c, 0x6c, 0x66, 0xfe, 0x60, 0x60, 0x00,
|
||||
0x00, 0x7e, 0x06, 0x7e, 0x60, 0x66, 0x3c, 0x00, 0x00, 0x3c, 0x06, 0x3e,
|
||||
0x66, 0x66, 0x3c, 0x00, 0x00, 0x7e, 0x30, 0x30, 0x18, 0x18, 0x18, 0x00,
|
||||
0x00, 0x3c, 0x66, 0x3c, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x3c, 0x66, 0x7c,
|
||||
0x60, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x18, 0x00,
|
||||
0x00, 0x00, 0x18, 0x00, 0x18, 0x18, 0x0c, 0x00, 0x00, 0x70, 0x1c, 0x06,
|
||||
0x06, 0x1c, 0x70, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x00, 0x00,
|
||||
0x00, 0x0e, 0x38, 0x60, 0x60, 0x38, 0x0e, 0x00, 0x00, 0x3c, 0x66, 0x30,
|
||||
0x18, 0x00, 0x18, 0x00, 0x00, 0x3c, 0x66, 0x76, 0x76, 0x06, 0x46, 0x3c,
|
||||
0x00, 0x3c, 0x66, 0x7e, 0x66, 0x66, 0x66, 0x00, 0x00, 0x3e, 0x66, 0x3e,
|
||||
0x66, 0x66, 0x3e, 0x00, 0x00, 0x3c, 0x66, 0x06, 0x06, 0x66, 0x3c, 0x00,
|
||||
0x00, 0x1e, 0x36, 0x66, 0x66, 0x36, 0x1e, 0x00, 0x00, 0x7e, 0x06, 0x1e,
|
||||
0x06, 0x06, 0x7e, 0x00, 0x00, 0x3e, 0x06, 0x1e, 0x06, 0x06, 0x06, 0x00,
|
||||
0x00, 0x3c, 0x66, 0x06, 0x76, 0x66, 0x3c, 0x00, 0x00, 0x66, 0x66, 0x7e,
|
||||
0x66, 0x66, 0x66, 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00,
|
||||
0x00, 0x78, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x66, 0x36, 0x1e,
|
||||
0x1e, 0x36, 0x66, 0x00, 0x00, 0x06, 0x06, 0x06, 0x06, 0x06, 0x7e, 0x00,
|
||||
0x00, 0x46, 0x6e, 0x7e, 0x56, 0x46, 0x46, 0x00, 0x00, 0x66, 0x6e, 0x7e,
|
||||
0x76, 0x66, 0x66, 0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00,
|
||||
0x00, 0x3e, 0x66, 0x3e, 0x06, 0x06, 0x06, 0x00, 0x00, 0x3c, 0x66, 0x66,
|
||||
0x66, 0x3c, 0x70, 0x00, 0x00, 0x3e, 0x66, 0x3e, 0x1e, 0x36, 0x66, 0x00,
|
||||
0x00, 0x3c, 0x66, 0x0c, 0x30, 0x66, 0x3c, 0x00, 0x00, 0x7e, 0x18, 0x18,
|
||||
0x18, 0x18, 0x18, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x00,
|
||||
0x00, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x46, 0x46, 0x56,
|
||||
0x7e, 0x6e, 0x46, 0x00, 0x00, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0x66, 0x00,
|
||||
0x00, 0x66, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x00, 0x7e, 0x30, 0x18,
|
||||
0x0c, 0x06, 0x7e, 0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c,
|
||||
0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x40, 0x00, 0x00, 0x3c, 0x30, 0x30,
|
||||
0x30, 0x30, 0x30, 0x3c, 0x00, 0x18, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x0c, 0x18, 0x30,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x60, 0x7c, 0x66, 0x7c, 0x00,
|
||||
0x00, 0x06, 0x06, 0x3e, 0x66, 0x66, 0x3e, 0x00, 0x00, 0x00, 0x3c, 0x06,
|
||||
0x06, 0x06, 0x3c, 0x00, 0x00, 0x60, 0x60, 0x7c, 0x66, 0x66, 0x7c, 0x00,
|
||||
0x00, 0x00, 0x3c, 0x66, 0x7e, 0x06, 0x3c, 0x00, 0x00, 0x38, 0x0c, 0x3e,
|
||||
0x0c, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x7c, 0x66, 0x7c, 0x40, 0x3c, 0x00,
|
||||
0x00, 0x06, 0x06, 0x3e, 0x66, 0x66, 0x66, 0x00, 0x00, 0x18, 0x00, 0x1c,
|
||||
0x18, 0x18, 0x3c, 0x00, 0x00, 0x30, 0x00, 0x30, 0x30, 0x30, 0x1e, 0x00,
|
||||
0x00, 0x06, 0x06, 0x36, 0x1e, 0x36, 0x66, 0x00, 0x00, 0x1c, 0x18, 0x18,
|
||||
0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x66, 0xfe, 0xfe, 0xd6, 0xc6, 0x00,
|
||||
0x00, 0x00, 0x3e, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x3c, 0x66,
|
||||
0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x3e, 0x66, 0x66, 0x3e, 0x06, 0x00,
|
||||
0x00, 0x00, 0x7c, 0x66, 0x66, 0x7c, 0x60, 0x00, 0x00, 0x00, 0x3e, 0x66,
|
||||
0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x7c, 0x06, 0x3c, 0x60, 0x3e, 0x00,
|
||||
0x00, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x66, 0x66,
|
||||
0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x00,
|
||||
0x00, 0x00, 0xc6, 0xd6, 0xfe, 0x7c, 0x6c, 0x00, 0x00, 0x00, 0x66, 0x3c,
|
||||
0x18, 0x3c, 0x66, 0x00, 0x00, 0x00, 0x66, 0x66, 0x7c, 0x60, 0x3c, 0x00,
|
||||
0x00, 0x00, 0x7e, 0x30, 0x18, 0x0c, 0x7e, 0x00, 0x00, 0x00, 0x18, 0x08,
|
||||
0x08, 0x04, 0x08, 0x08, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
|
||||
0x00, 0x00, 0x0c, 0x08, 0x08, 0x10, 0x08, 0x08
|
||||
};
|
450
ios_mcp/source/fsa.c
Normal file
450
ios_mcp/source/fsa.c
Normal file
@ -0,0 +1,450 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "imports.h"
|
||||
#include "fsa.h"
|
||||
|
||||
static void* allocIobuf()
|
||||
{
|
||||
void* ptr = IOS_HeapAlloc(0xcaff, 0x828);
|
||||
|
||||
memset(ptr, 0x00, 0x828);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void freeIobuf(void* ptr)
|
||||
{
|
||||
IOS_HeapFree(0xcaff, ptr);
|
||||
}
|
||||
|
||||
int FSA_Mount(int fd, const char* device_path, char* volume_path, uint32_t flags, char* arg_string, int arg_string_len)
|
||||
{
|
||||
uint8_t* iobuf = allocIobuf();
|
||||
uint8_t* inbuf8 = iobuf;
|
||||
uint8_t* outbuf8 = &iobuf[0x520];
|
||||
IOSVec_t* iovec = (IOSVec_t*)&iobuf[0x7C0];
|
||||
uint32_t* inbuf = (uint32_t*)inbuf8;
|
||||
uint32_t* outbuf = (uint32_t*)outbuf8;
|
||||
|
||||
strncpy((char*)&inbuf8[0x04], device_path, 0x27F);
|
||||
strncpy((char*)&inbuf8[0x284], volume_path, 0x27F);
|
||||
inbuf[0x504 / 4] = (uint32_t)flags;
|
||||
inbuf[0x508 / 4] = (uint32_t)arg_string_len;
|
||||
|
||||
iovec[0].ptr = inbuf;
|
||||
iovec[0].len = 0x520;
|
||||
iovec[1].ptr = arg_string;
|
||||
iovec[1].len = arg_string_len;
|
||||
iovec[2].ptr = outbuf;
|
||||
iovec[2].len = 0x293;
|
||||
|
||||
int ret = IOS_Ioctlv(fd, 0x01, 2, 1, iovec);
|
||||
|
||||
freeIobuf(iobuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int FSA_Unmount(int fd, const char* path, uint32_t flags)
|
||||
{
|
||||
uint8_t* iobuf = allocIobuf();
|
||||
uint32_t* inbuf = (uint32_t*)iobuf;
|
||||
uint32_t* outbuf = (uint32_t*)&iobuf[0x520];
|
||||
|
||||
strncpy((char*)&inbuf[0x01], path, 0x27F);
|
||||
inbuf[0x284 / 4] = flags;
|
||||
|
||||
int ret = IOS_Ioctl(fd, 0x02, inbuf, 0x520, outbuf, 0x293);
|
||||
|
||||
freeIobuf(iobuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int FSA_FlushVolume(int fd, const char* volume_path)
|
||||
{
|
||||
uint8_t* iobuf = allocIobuf();
|
||||
uint32_t* inbuf = (uint32_t*)iobuf;
|
||||
uint32_t* outbuf = (uint32_t*)&iobuf[0x520];
|
||||
|
||||
strncpy((char*)&inbuf[0x01], volume_path, 0x27F);
|
||||
|
||||
int ret = IOS_Ioctl(fd, 0x1B, inbuf, 0x520, outbuf, 0x293);
|
||||
|
||||
freeIobuf(iobuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int FSA_MakeDir(int fd, const char* path, uint32_t flags)
|
||||
{
|
||||
uint8_t* iobuf = allocIobuf();
|
||||
uint32_t* inbuf = (uint32_t*)iobuf;
|
||||
uint32_t* outbuf = (uint32_t*)&iobuf[0x520];
|
||||
|
||||
strncpy((char*)&inbuf[0x01], path, 0x27F);
|
||||
inbuf[0x284 / 4] = flags;
|
||||
|
||||
int ret = IOS_Ioctl(fd, 0x07, inbuf, 0x520, outbuf, 0x293);
|
||||
|
||||
freeIobuf(iobuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int FSA_OpenDir(int fd, const char* path, int* outHandle)
|
||||
{
|
||||
uint8_t* iobuf = allocIobuf();
|
||||
uint32_t* inbuf = (uint32_t*)iobuf;
|
||||
uint32_t* outbuf = (uint32_t*)&iobuf[0x520];
|
||||
|
||||
strncpy((char*)&inbuf[0x01], path, 0x27F);
|
||||
|
||||
int ret = IOS_Ioctl(fd, 0x0A, inbuf, 0x520, outbuf, 0x293);
|
||||
|
||||
if(outHandle) *outHandle = outbuf[1];
|
||||
|
||||
freeIobuf(iobuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int FSA_ReadDir(int fd, int handle, FSDirectoryEntry* out_data)
|
||||
{
|
||||
uint8_t* iobuf = allocIobuf();
|
||||
uint32_t* inbuf = (uint32_t*)iobuf;
|
||||
uint32_t* outbuf = (uint32_t*)&iobuf[0x520];
|
||||
|
||||
inbuf[1] = handle;
|
||||
|
||||
int ret = IOS_Ioctl(fd, 0x0B, inbuf, 0x520, outbuf, 0x293);
|
||||
|
||||
if(out_data) memcpy(out_data, &outbuf[1], sizeof(FSDirectoryEntry));
|
||||
|
||||
freeIobuf(iobuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int FSA_RewindDir(int fd, int handle)
|
||||
{
|
||||
uint8_t* iobuf = allocIobuf();
|
||||
uint32_t* inbuf = (uint32_t*)iobuf;
|
||||
uint32_t* outbuf = (uint32_t*)&iobuf[0x520];
|
||||
|
||||
inbuf[1] = handle;
|
||||
|
||||
int ret = IOS_Ioctl(fd, 0x0C, inbuf, 0x520, outbuf, 0x293);
|
||||
|
||||
freeIobuf(iobuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int FSA_CloseDir(int fd, int handle)
|
||||
{
|
||||
uint8_t* iobuf = allocIobuf();
|
||||
uint32_t* inbuf = (uint32_t*)iobuf;
|
||||
uint32_t* outbuf = (uint32_t*)&iobuf[0x520];
|
||||
|
||||
inbuf[1] = handle;
|
||||
|
||||
int ret = IOS_Ioctl(fd, 0x0D, inbuf, 0x520, outbuf, 0x293);
|
||||
|
||||
freeIobuf(iobuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int FSA_ChangeDir(int fd, const char* path)
|
||||
{
|
||||
uint8_t* iobuf = allocIobuf();
|
||||
uint32_t* inbuf = (uint32_t*)iobuf;
|
||||
uint32_t* outbuf = (uint32_t*)&iobuf[0x520];
|
||||
|
||||
strncpy((char*)&inbuf[0x01], path, 0x27F);
|
||||
|
||||
int ret = IOS_Ioctl(fd, 0x05, inbuf, 0x520, outbuf, 0x293);
|
||||
|
||||
freeIobuf(iobuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int FSA_OpenFile(int fd, const char* path, const char* mode, int* outHandle)
|
||||
{
|
||||
uint8_t* iobuf = allocIobuf();
|
||||
uint32_t* inbuf = (uint32_t*)iobuf;
|
||||
uint32_t* outbuf = (uint32_t*)&iobuf[0x520];
|
||||
|
||||
strncpy((char*)&inbuf[0x01], path, 0x27F);
|
||||
strncpy((char*)&inbuf[0xA1], mode, 0x10);
|
||||
|
||||
int ret = IOS_Ioctl(fd, 0x0E, inbuf, 0x520, outbuf, 0x293);
|
||||
|
||||
if(outHandle) *outHandle = outbuf[1];
|
||||
|
||||
freeIobuf(iobuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int _FSA_ReadWriteFile(int fd, void* data, uint32_t size, uint32_t cnt, int fileHandle, uint32_t flags, int read)
|
||||
{
|
||||
uint8_t* iobuf = allocIobuf();
|
||||
uint8_t* inbuf8 = iobuf;
|
||||
uint8_t* outbuf8 = &iobuf[0x520];
|
||||
IOSVec_t* iovec = (IOSVec_t*)&iobuf[0x7C0];
|
||||
uint32_t* inbuf = (uint32_t*)inbuf8;
|
||||
uint32_t* outbuf = (uint32_t*)outbuf8;
|
||||
|
||||
inbuf[0x08 / 4] = size;
|
||||
inbuf[0x0C / 4] = cnt;
|
||||
inbuf[0x14 / 4] = fileHandle;
|
||||
inbuf[0x18 / 4] = flags;
|
||||
|
||||
iovec[0].ptr = inbuf;
|
||||
iovec[0].len = 0x520;
|
||||
|
||||
iovec[1].ptr = data;
|
||||
iovec[1].len = size * cnt;
|
||||
|
||||
iovec[2].ptr = outbuf;
|
||||
iovec[2].len = 0x293;
|
||||
|
||||
int ret;
|
||||
if(read) ret = IOS_Ioctlv(fd, 0x0F, 1, 2, iovec);
|
||||
else ret = IOS_Ioctlv(fd, 0x10, 2, 1, iovec);
|
||||
|
||||
freeIobuf(iobuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int FSA_ReadFile(int fd, void* data, uint32_t size, uint32_t cnt, int fileHandle, uint32_t flags)
|
||||
{
|
||||
return _FSA_ReadWriteFile(fd, data, size, cnt, fileHandle, flags, 1);
|
||||
}
|
||||
|
||||
int FSA_WriteFile(int fd, void* data, uint32_t size, uint32_t cnt, int fileHandle, uint32_t flags)
|
||||
{
|
||||
return _FSA_ReadWriteFile(fd, data, size, cnt, fileHandle, flags, 0);
|
||||
}
|
||||
|
||||
int FSA_StatFile(int fd, int handle, FSStat* out_data)
|
||||
{
|
||||
uint8_t* iobuf = allocIobuf();
|
||||
uint32_t* inbuf = (uint32_t*)iobuf;
|
||||
uint32_t* outbuf = (uint32_t*)&iobuf[0x520];
|
||||
|
||||
inbuf[1] = handle;
|
||||
|
||||
int ret = IOS_Ioctl(fd, 0x14, inbuf, 0x520, outbuf, 0x293);
|
||||
|
||||
if(out_data) memcpy(out_data, &outbuf[1], sizeof(FSStat));
|
||||
|
||||
freeIobuf(iobuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int FSA_CloseFile(int fd, int fileHandle)
|
||||
{
|
||||
uint8_t* iobuf = allocIobuf();
|
||||
uint32_t* inbuf = (uint32_t*)iobuf;
|
||||
uint32_t* outbuf = (uint32_t*)&iobuf[0x520];
|
||||
|
||||
inbuf[1] = fileHandle;
|
||||
|
||||
int ret = IOS_Ioctl(fd, 0x15, inbuf, 0x520, outbuf, 0x293);
|
||||
|
||||
freeIobuf(iobuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int FSA_SetPosFile(int fd, int fileHandle, uint32_t position)
|
||||
{
|
||||
uint8_t* iobuf = allocIobuf();
|
||||
uint32_t* inbuf = (uint32_t*)iobuf;
|
||||
uint32_t* outbuf = (uint32_t*)&iobuf[0x520];
|
||||
|
||||
inbuf[1] = fileHandle;
|
||||
inbuf[2] = position;
|
||||
|
||||
int ret = IOS_Ioctl(fd, 0x12, inbuf, 0x520, outbuf, 0x293);
|
||||
|
||||
freeIobuf(iobuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int FSA_GetStat(int fd, const char *path, FSStat* out_data)
|
||||
{
|
||||
uint8_t* iobuf = allocIobuf();
|
||||
uint32_t* inbuf = (uint32_t*)iobuf;
|
||||
uint32_t* outbuf = (uint32_t*)&iobuf[0x520];
|
||||
|
||||
strncpy((char*)&inbuf[0x01], path, 0x27F);
|
||||
inbuf[0x284/4] = 5;
|
||||
|
||||
int ret = IOS_Ioctl(fd, 0x18, inbuf, 0x520, outbuf, 0x293);
|
||||
|
||||
if(out_data) memcpy(out_data, &outbuf[1], sizeof(FSStat));
|
||||
|
||||
freeIobuf(iobuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int FSA_Remove(int fd, const char *path)
|
||||
{
|
||||
uint8_t* iobuf = allocIobuf();
|
||||
uint32_t* inbuf = (uint32_t*)iobuf;
|
||||
uint32_t* outbuf = (uint32_t*)&iobuf[0x520];
|
||||
|
||||
strncpy((char*)&inbuf[0x01], path, 0x27F);
|
||||
|
||||
int ret = IOS_Ioctl(fd, 0x08, inbuf, 0x520, outbuf, 0x293);
|
||||
|
||||
freeIobuf(iobuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int FSA_ChangeMode(int fd, const char *path, int mode)
|
||||
{
|
||||
uint8_t* iobuf = allocIobuf();
|
||||
uint32_t* inbuf = (uint32_t*)iobuf;
|
||||
uint32_t* outbuf = (uint32_t*)&iobuf[0x520];
|
||||
|
||||
strncpy((char*)&inbuf[0x01], path, 0x27F);
|
||||
inbuf[0x284/4] = mode;
|
||||
inbuf[0x288/4] = 0x777; // mask
|
||||
|
||||
int ret = IOS_Ioctl(fd, 0x20, inbuf, 0x520, outbuf, 0x293);
|
||||
|
||||
freeIobuf(iobuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// type 4 :
|
||||
// 0x08 : device size in sectors (uint64_t)
|
||||
// 0x10 : device sector size (uint32_t)
|
||||
int FSA_GetDeviceInfo(int fd, const char* device_path, int type, uint32_t* out_data)
|
||||
{
|
||||
uint8_t* iobuf = allocIobuf();
|
||||
uint32_t* inbuf = (uint32_t*)iobuf;
|
||||
uint32_t* outbuf = (uint32_t*)&iobuf[0x520];
|
||||
|
||||
strncpy((char*)&inbuf[0x01], device_path, 0x27F);
|
||||
inbuf[0x284 / 4] = type;
|
||||
|
||||
int ret = IOS_Ioctl(fd, 0x18, inbuf, 0x520, outbuf, 0x293);
|
||||
|
||||
int size = 0;
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case 0: case 1: case 7:
|
||||
size = 0x8;
|
||||
break;
|
||||
case 2:
|
||||
size = 0x4;
|
||||
break;
|
||||
case 3:
|
||||
size = 0x1E;
|
||||
break;
|
||||
case 4:
|
||||
size = 0x28;
|
||||
break;
|
||||
case 5:
|
||||
size = 0x64;
|
||||
break;
|
||||
case 6: case 8:
|
||||
size = 0x14;
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(out_data, &outbuf[1], size);
|
||||
|
||||
freeIobuf(iobuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int FSA_RawOpen(int fd, const char* device_path, int* outHandle)
|
||||
{
|
||||
uint8_t* iobuf = allocIobuf();
|
||||
uint32_t* inbuf = (uint32_t*)iobuf;
|
||||
uint32_t* outbuf = (uint32_t*)&iobuf[0x520];
|
||||
|
||||
strncpy((char*)&inbuf[0x01], device_path, 0x27F);
|
||||
|
||||
int ret = IOS_Ioctl(fd, 0x6A, inbuf, 0x520, outbuf, 0x293);
|
||||
|
||||
if(outHandle) *outHandle = outbuf[1];
|
||||
|
||||
freeIobuf(iobuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int FSA_RawClose(int fd, int device_handle)
|
||||
{
|
||||
uint8_t* iobuf = allocIobuf();
|
||||
uint32_t* inbuf = (uint32_t*)iobuf;
|
||||
uint32_t* outbuf = (uint32_t*)&iobuf[0x520];
|
||||
|
||||
inbuf[1] = device_handle;
|
||||
|
||||
int ret = IOS_Ioctl(fd, 0x6D, inbuf, 0x520, outbuf, 0x293);
|
||||
|
||||
freeIobuf(iobuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// offset in blocks of 0x1000 bytes
|
||||
int FSA_RawRead(int fd, void* data, uint32_t size_bytes, uint32_t cnt, uint64_t blocks_offset, int device_handle)
|
||||
{
|
||||
uint8_t* iobuf = allocIobuf();
|
||||
uint8_t* inbuf8 = iobuf;
|
||||
uint8_t* outbuf8 = &iobuf[0x520];
|
||||
IOSVec_t* iovec = (IOSVec_t*)&iobuf[0x7C0];
|
||||
uint32_t* inbuf = (uint32_t*)inbuf8;
|
||||
uint32_t* outbuf = (uint32_t*)outbuf8;
|
||||
|
||||
// note : offset_bytes = blocks_offset * size_bytes
|
||||
inbuf[0x08 / 4] = (blocks_offset >> 32);
|
||||
inbuf[0x0C / 4] = (blocks_offset & 0xFFFFFFFF);
|
||||
inbuf[0x10 / 4] = cnt;
|
||||
inbuf[0x14 / 4] = size_bytes;
|
||||
inbuf[0x18 / 4] = device_handle;
|
||||
|
||||
iovec[0].ptr = inbuf;
|
||||
iovec[0].len = 0x520;
|
||||
|
||||
iovec[1].ptr = data;
|
||||
iovec[1].len = size_bytes * cnt;
|
||||
|
||||
iovec[2].ptr = outbuf;
|
||||
iovec[2].len = 0x293;
|
||||
|
||||
int ret = IOS_Ioctlv(fd, 0x6B, 1, 2, iovec);
|
||||
|
||||
freeIobuf(iobuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int FSA_RawWrite(int fd, void* data, uint32_t size_bytes, uint32_t cnt, uint64_t blocks_offset, int device_handle)
|
||||
{
|
||||
uint8_t* iobuf = allocIobuf();
|
||||
uint8_t* inbuf8 = iobuf;
|
||||
uint8_t* outbuf8 = &iobuf[0x520];
|
||||
IOSVec_t* iovec = (IOSVec_t*)&iobuf[0x7C0];
|
||||
uint32_t* inbuf = (uint32_t*)inbuf8;
|
||||
uint32_t* outbuf = (uint32_t*)outbuf8;
|
||||
|
||||
inbuf[0x08 / 4] = (blocks_offset >> 32);
|
||||
inbuf[0x0C / 4] = (blocks_offset & 0xFFFFFFFF);
|
||||
inbuf[0x10 / 4] = cnt;
|
||||
inbuf[0x14 / 4] = size_bytes;
|
||||
inbuf[0x18 / 4] = device_handle;
|
||||
|
||||
iovec[0].ptr = inbuf;
|
||||
iovec[0].len = 0x520;
|
||||
|
||||
iovec[1].ptr = data;
|
||||
iovec[1].len = size_bytes * cnt;
|
||||
|
||||
iovec[2].ptr = outbuf;
|
||||
iovec[2].len = 0x293;
|
||||
|
||||
int ret = IOS_Ioctlv(fd, 0x6C, 2, 1, iovec);
|
||||
|
||||
freeIobuf(iobuf);
|
||||
return ret;
|
||||
}
|
54
ios_mcp/source/fsa.h
Normal file
54
ios_mcp/source/fsa.h
Normal file
@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
#include "imports.h"
|
||||
|
||||
typedef struct {
|
||||
uint32_t flags;
|
||||
uint32_t mode;
|
||||
uint32_t owner;
|
||||
uint32_t group;
|
||||
uint32_t size;
|
||||
uint32_t allocSize;
|
||||
uint32_t unk[3];
|
||||
uint32_t id;
|
||||
uint32_t ctime;
|
||||
uint32_t mtime;
|
||||
uint32_t unk2[0x0D];
|
||||
} FSStat;
|
||||
|
||||
typedef struct {
|
||||
FSStat stat;
|
||||
char name[0x100];
|
||||
} FSDirectoryEntry;
|
||||
|
||||
#define DIR_ENTRY_IS_DIRECTORY 0x80000000
|
||||
|
||||
#define FSA_MOUNTFLAGS_BINDMOUNT (1 << 0)
|
||||
#define FSA_MOUNTFLAGS_GLOBAL (1 << 1)
|
||||
|
||||
int FSA_Mount(int fd, const char* device_path, char* volume_path, uint32_t flags, char* arg_string, int arg_string_len);
|
||||
int FSA_Unmount(int fd, const char* path, uint32_t flags);
|
||||
int FSA_FlushVolume(int fd, const char* volume_path);
|
||||
|
||||
int FSA_GetDeviceInfo(int fd, const char* device_path, int type, uint32_t* out_data);
|
||||
|
||||
int FSA_MakeDir(int fd, const char* path, uint32_t flags);
|
||||
int FSA_OpenDir(int fd, const char* path, int* outHandle);
|
||||
int FSA_ReadDir(int fd, int handle, FSDirectoryEntry* out_data);
|
||||
int FSA_RewindDir(int fd, int handle);
|
||||
int FSA_CloseDir(int fd, int handle);
|
||||
int FSA_ChangeDir(int fd, const char* path);
|
||||
|
||||
int FSA_OpenFile(int fd, const char* path, const char* mode, int* outHandle);
|
||||
int FSA_ReadFile(int fd, void* data, uint32_t size, uint32_t cnt, int fileHandle, uint32_t flags);
|
||||
int FSA_WriteFile(int fd, void* data, uint32_t size, uint32_t cnt, int fileHandle, uint32_t flags);
|
||||
int FSA_StatFile(int fd, int handle, FSStat* out_data);
|
||||
int FSA_CloseFile(int fd, int fileHandle);
|
||||
int FSA_SetPosFile(int fd, int fileHandle, uint32_t position);
|
||||
int FSA_GetStat(int fd, const char* path, FSStat* out_data);
|
||||
int FSA_Remove(int fd, const char* path);
|
||||
int FSA_ChangeMode(int fd, const char* path, int mode);
|
||||
|
||||
int FSA_RawOpen(int fd, const char* device_path, int* outHandle);
|
||||
int FSA_RawRead(int fd, void* data, uint32_t size_bytes, uint32_t cnt, uint64_t sector_offset, int device_handle);
|
||||
int FSA_RawWrite(int fd, void* data, uint32_t size_bytes, uint32_t cnt, uint64_t sector_offset, int device_handle);
|
||||
int FSA_RawClose(int fd, int device_handle);
|
126
ios_mcp/source/gfx.c
Normal file
126
ios_mcp/source/gfx.c
Normal file
@ -0,0 +1,126 @@
|
||||
#include "gfx.h"
|
||||
#include "imports.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "font_bin.h"
|
||||
|
||||
#define TV_FRAMEBUFFER ((uint32_t*) (0x14000000 + 0x3500000))
|
||||
#define TV_HEIGHT 720
|
||||
#define TV_STRIDE 1280
|
||||
|
||||
#define DRC_FRAMEBUFFER ((uint32_t*) (0x14000000 + 0x38c0000))
|
||||
#define DRC_HEIGHT 480
|
||||
#define DRC_STRIDE 896
|
||||
|
||||
#define CHAR_SIZE_X 8
|
||||
#define CHAR_SIZE_Y 8
|
||||
|
||||
static uint32_t font_color = 0xffffffff;
|
||||
|
||||
void gfx_clear(uint32_t col)
|
||||
{
|
||||
for (uint32_t i = 0; i < TV_STRIDE * TV_HEIGHT; i++) {
|
||||
TV_FRAMEBUFFER[i] = col;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < DRC_STRIDE * DRC_HEIGHT; i++) {
|
||||
DRC_FRAMEBUFFER[i] = col;
|
||||
}
|
||||
}
|
||||
|
||||
void gfx_draw_pixel(uint32_t x, uint32_t y, uint32_t col)
|
||||
{
|
||||
// put pixel in the drc buffer
|
||||
uint32_t i = x + y * DRC_STRIDE;
|
||||
if (i < DRC_STRIDE * DRC_HEIGHT) {
|
||||
DRC_FRAMEBUFFER[i] = col;
|
||||
}
|
||||
|
||||
// scale and put pixel in the tv buffer
|
||||
for (uint32_t yy = (y * 1.5f); yy < ((y * 1.5f) + 1); yy++) {
|
||||
for (uint32_t xx = (x * 1.5f); xx < ((x * 1.5f) + 1); xx++) {
|
||||
uint32_t i = xx + yy * TV_STRIDE;
|
||||
if (i < TV_STRIDE * TV_HEIGHT) {
|
||||
TV_FRAMEBUFFER[i] = col;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gfx_draw_rect_filled(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t col)
|
||||
{
|
||||
for (uint32_t yy = y; yy < y + h; yy++) {
|
||||
for (uint32_t xx = x; xx < x + w; xx++) {
|
||||
gfx_draw_pixel(xx, yy, col);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gfx_draw_rect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t borderSize, uint32_t col)
|
||||
{
|
||||
gfx_draw_rect_filled(x, y, w, borderSize, col);
|
||||
gfx_draw_rect_filled(x, y + h - borderSize, w, borderSize, col);
|
||||
gfx_draw_rect_filled(x, y, borderSize, h, col);
|
||||
gfx_draw_rect_filled(x + w - borderSize, y, borderSize, h, col);
|
||||
}
|
||||
|
||||
void gfx_set_font_color(uint32_t col)
|
||||
{
|
||||
font_color = col;
|
||||
}
|
||||
|
||||
uint32_t gfx_get_text_width(const char* string)
|
||||
{
|
||||
uint32_t i;
|
||||
for (i = 0; string[i]; i++);
|
||||
return i * CHAR_SIZE_X;
|
||||
}
|
||||
|
||||
static void gfx_draw_char(uint32_t x, uint32_t y, char c)
|
||||
{
|
||||
if(c < 32) {
|
||||
return;
|
||||
}
|
||||
|
||||
c -= 32;
|
||||
|
||||
const uint8_t* charData = &font_bin[(CHAR_SIZE_X * CHAR_SIZE_Y * c) / 8];
|
||||
|
||||
for (uint32_t i = 0; i < CHAR_SIZE_Y; i++) {
|
||||
uint8_t v = *(charData++);
|
||||
|
||||
for (uint32_t j = 0; j < CHAR_SIZE_X; j++) {
|
||||
if(v & (1 << j)) {
|
||||
gfx_draw_pixel(x + j, y + i, font_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gfx_print(uint32_t x, uint32_t y, int alignRight, const char* string)
|
||||
{
|
||||
if (alignRight) {
|
||||
x -= gfx_get_text_width(string);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; string[i]; i++) {
|
||||
if(string[i] >= 32 && string[i] < 128) {
|
||||
gfx_draw_char(x + i * CHAR_SIZE_X, y, string[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gfx_printf(uint32_t x, uint32_t y, int alignRight, const char* format, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
|
||||
char buffer[0x100];
|
||||
|
||||
vsnprintf(buffer, sizeof(buffer), format, args);
|
||||
gfx_print(x, y, alignRight, buffer);
|
||||
|
||||
va_end(args);
|
||||
}
|
23
ios_mcp/source/gfx.h
Normal file
23
ios_mcp/source/gfx.h
Normal file
@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// visible screen sizes
|
||||
#define SCREEN_WIDTH 854
|
||||
#define SCREEN_HEIGHT 480
|
||||
|
||||
void gfx_clear(uint32_t color);
|
||||
|
||||
void gfx_draw_pixel(uint32_t x, uint32_t y, uint32_t color);
|
||||
|
||||
void gfx_draw_rect_filled(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t color);
|
||||
|
||||
void gfx_draw_rect(uint32_t x, uint32_t y, uint32_t w, uint32_t h, uint32_t borderSize, uint32_t col);
|
||||
|
||||
void gfx_set_font_color(uint32_t col);
|
||||
|
||||
uint32_t gfx_get_text_width(const char* string);
|
||||
|
||||
void gfx_print(uint32_t x, uint32_t y, int alignRight, const char* string);
|
||||
|
||||
void gfx_printf(uint32_t x, uint32_t y, int alignRight, const char* format, ...);
|
42
ios_mcp/source/imports.h
Normal file
42
ios_mcp/source/imports.h
Normal file
@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
typedef struct {
|
||||
void* ptr;
|
||||
uint32_t len;
|
||||
uint32_t paddr;
|
||||
} IOSVec_t;
|
||||
|
||||
// thumb functions can't just be provided to the linker
|
||||
#define setDefaultTitleId ((int (*)(uint64_t tid)) (0x0510d984 | 1))
|
||||
|
||||
int bspWrite(const char* entity, uint32_t instance, const char* attribute, uint32_t size, const void* buffer);
|
||||
int bspRead(const char* entity, uint32_t instance, const char* attribute, uint32_t size, void* buffer);
|
||||
|
||||
int IOS_CreateThread(int (*fun)(void* arg), void* arg, void* stack_top, uint32_t stacksize, int priority, uint32_t flags);
|
||||
int IOS_JoinThread(int threadid, int* retval);
|
||||
int IOS_CancelThread(int threadid, int return_value);
|
||||
int IOS_StartThread(int threadid);
|
||||
int IOS_GetThreadPriority(int threadid);
|
||||
|
||||
int IOS_CreateMessageQueue(uint32_t* ptr, uint32_t n_msgs);
|
||||
int IOS_DestroyMessageQueue(int queueid);
|
||||
int IOS_ReceiveMessage(int queueid, uint32_t* message, uint32_t flags);
|
||||
|
||||
void* IOS_HeapAlloc(uint32_t heap, uint32_t size);
|
||||
void* IOS_HeapAllocAligned(uint32_t heap, uint32_t size, uint32_t alignment);
|
||||
void IOS_HeapFree(uint32_t heap, void* ptr);
|
||||
|
||||
int IOS_Open(const char* device, int mode);
|
||||
int IOS_Close(int fd);
|
||||
int IOS_Ioctl(int fd, uint32_t request, void* input_buffer, uint32_t input_buffer_len, void* output_buffer, uint32_t output_buffer_len);
|
||||
int IOS_Ioctlv(int fd, uint32_t request, uint32_t vector_count_in, uint32_t vector_count_out, IOSVec_t* vector);
|
||||
int IOS_IoctlvAsync(int fd, uint32_t request, uint32_t vector_count_in, uint32_t vector_count_out, IOSVec_t* vector, int callbackQueue, void* msg);
|
||||
|
||||
void IOS_InvalidateDCache(void* ptr, uint32_t len);
|
||||
void IOS_FlushDCache(void* ptr, uint32_t len);
|
||||
|
||||
void IOS_Shutdown(int reset);
|
||||
int IOS_Syscall0x81(int type, uint32_t address, uint32_t value);
|
115
ios_mcp/source/mcp_install.c
Normal file
115
ios_mcp/source/mcp_install.c
Normal file
@ -0,0 +1,115 @@
|
||||
#include "mcp_install.h"
|
||||
#include "imports.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static void* allocIoBuf(uint32_t size)
|
||||
{
|
||||
void* ptr = IOS_HeapAlloc(0xcaff, size);
|
||||
memset(ptr, 0, size);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void freeIoBuf(void* ptr)
|
||||
{
|
||||
IOS_HeapFree(0xcaff, ptr);
|
||||
}
|
||||
|
||||
int MCP_InstallGetInfo(int handle, const char* path, MCPInstallInfo* out_info)
|
||||
{
|
||||
uint8_t* buf = allocIoBuf(0x27f + 0x16 + (sizeof(IOSVec_t) * 2));
|
||||
|
||||
char* path_buf = (char*) buf + (sizeof(IOSVec_t) * 2);
|
||||
strncpy(path_buf, path, 0x27f);
|
||||
|
||||
void* out_buf = buf + (sizeof(IOSVec_t) * 2) + 0x27f;
|
||||
|
||||
IOSVec_t* vecs = (IOSVec_t*) buf;
|
||||
vecs[0].ptr = path_buf;
|
||||
vecs[0].len = 0x27f;
|
||||
vecs[1].ptr = out_buf;
|
||||
vecs[1].len = 0x16;
|
||||
|
||||
int res = IOS_Ioctlv(handle, 0x80, 1, 1, vecs);
|
||||
if (res >= 0) {
|
||||
memcpy(out_info, out_buf, 0x16);
|
||||
}
|
||||
|
||||
freeIoBuf(buf);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int MCP_SetTargetUsb(int handle, int target)
|
||||
{
|
||||
uint32_t* buf = allocIoBuf(4);
|
||||
memcpy(buf, &target, 4);
|
||||
|
||||
int res = IOS_Ioctl(handle, 0xf1, buf, 4, NULL, 0);
|
||||
|
||||
freeIoBuf(buf);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int MCP_InstallSetTargetDevice(int handle, int device)
|
||||
{
|
||||
uint32_t* buf = allocIoBuf(4);
|
||||
memcpy(buf, &device, 4);
|
||||
|
||||
int res = IOS_Ioctl(handle, 0x8d, buf, 4, NULL, 0);
|
||||
|
||||
freeIoBuf(buf);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int MCP_InstallTitle(int handle, const char* path)
|
||||
{
|
||||
uint8_t* buf = allocIoBuf(0x27f + sizeof(IOSVec_t));
|
||||
|
||||
char* path_buf = (char*) buf + sizeof(IOSVec_t);
|
||||
strncpy(path_buf, path, 0x27f);
|
||||
|
||||
IOSVec_t* vecs = (IOSVec_t*) buf;
|
||||
vecs[0].ptr = path_buf;
|
||||
vecs[0].len = 0x27f;
|
||||
|
||||
int res = IOS_Ioctlv(handle, 0x81, 1, 0, vecs);
|
||||
|
||||
freeIoBuf(buf);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int MCP_InstallTitleAsync(int handle, const char* path, int callbackQueue, void* msg)
|
||||
{
|
||||
uint8_t* buf = allocIoBuf(0x27f + sizeof(IOSVec_t));
|
||||
|
||||
char* path_buf = (char*) buf + sizeof(IOSVec_t);
|
||||
strncpy(path_buf, path, 0x27f);
|
||||
|
||||
IOSVec_t* vecs = (IOSVec_t*) buf;
|
||||
vecs[0].ptr = path_buf;
|
||||
vecs[0].len = 0x27f;
|
||||
|
||||
int res = IOS_IoctlvAsync(handle, 0x81, 1, 0, vecs, callbackQueue, msg);
|
||||
|
||||
freeIoBuf(buf);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int MCP_InstallGetProgress(int handle, MCPInstallProgress* progress)
|
||||
{
|
||||
uint32_t* buf = allocIoBuf(0x24);
|
||||
|
||||
int res = IOS_Ioctl(handle, 0x82, NULL, 0, buf, 0x24);
|
||||
if (res >= 0) {
|
||||
memcpy(progress, buf, 0x24);
|
||||
}
|
||||
|
||||
freeIoBuf(buf);
|
||||
|
||||
return res;
|
||||
}
|
29
ios_mcp/source/mcp_install.h
Normal file
29
ios_mcp/source/mcp_install.h
Normal file
@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint64_t titleId;
|
||||
char unk[14];
|
||||
} MCPInstallInfo;
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
uint32_t inProgress;
|
||||
uint64_t titleId;
|
||||
uint64_t sizeTotal;
|
||||
uint64_t sizeProgress;
|
||||
uint32_t contentsTotal;
|
||||
uint32_t contentsProgress;
|
||||
} MCPInstallProgress;
|
||||
|
||||
int MCP_InstallGetInfo(int handle, const char* path, MCPInstallInfo* out_info);
|
||||
|
||||
int MCP_SetTargetUsb(int handle, int target);
|
||||
|
||||
int MCP_InstallSetTargetDevice(int handle, int device);
|
||||
|
||||
int MCP_InstallTitle(int handle, const char* path);
|
||||
|
||||
int MCP_InstallTitleAsync(int handle, const char* path, int callbackQueue, void* msg);
|
||||
|
||||
int MCP_InstallGetProgress(int handle, MCPInstallProgress* progress);
|
22
ios_mcp/source/mcp_ioctl100.c
Normal file
22
ios_mcp/source/mcp_ioctl100.c
Normal file
@ -0,0 +1,22 @@
|
||||
#include "imports.h"
|
||||
#include "menu.h"
|
||||
|
||||
static int threadStarted = 0;
|
||||
static uint8_t threadStack[0x1000] __attribute__((aligned(0x20)));
|
||||
|
||||
int MCP_ioctl100_patch(void* msg)
|
||||
{
|
||||
printf("MCP_ioctl100_patch %p\n", msg);
|
||||
|
||||
// start the menu thread
|
||||
if (!threadStarted) {
|
||||
int tid = IOS_CreateThread(menuThread, NULL, threadStack + sizeof(threadStack), sizeof(threadStack), IOS_GetThreadPriority(0), 1);
|
||||
if (tid > 0) {
|
||||
IOS_StartThread(tid);
|
||||
}
|
||||
|
||||
threadStarted = 1;
|
||||
}
|
||||
|
||||
return 29;
|
||||
}
|
10
ios_mcp/source/mcp_ioctl100_asm.s
Normal file
10
ios_mcp/source/mcp_ioctl100_asm.s
Normal file
@ -0,0 +1,10 @@
|
||||
.extern MCP_ioctl100_patch
|
||||
.global _MCP_ioctl100_patch
|
||||
_MCP_ioctl100_patch:
|
||||
.thumb
|
||||
ldr r0, [r7, #0xc]
|
||||
bx pc
|
||||
nop
|
||||
.arm
|
||||
ldr r12, =MCP_ioctl100_patch
|
||||
bx r12
|
864
ios_mcp/source/menu.c
Normal file
864
ios_mcp/source/menu.c
Normal file
@ -0,0 +1,864 @@
|
||||
/*
|
||||
* Copyright (C) 2022 GaryOderNichts
|
||||
*
|
||||
* 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, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "menu.h"
|
||||
#include "imports.h"
|
||||
#include "gfx.h"
|
||||
#include "utils.h"
|
||||
#include "fsa.h"
|
||||
#include "socket.h"
|
||||
#include "netconf.h"
|
||||
#include "wupserver.h"
|
||||
#include "mcp_install.h"
|
||||
#include "ccr.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define COLOR_BACKGROUND 0x000000ff
|
||||
#define COLOR_PRIMARY 0xffffffff
|
||||
#define COLOR_SECONDARY 0x3478e4ff
|
||||
#define COLOR_SUCCESS 0x00ff00ff
|
||||
#define COLOR_ERROR 0xff0000ff
|
||||
|
||||
static void option_SetColdbootTitle(void);
|
||||
static void option_DumpSyslogs(void);
|
||||
static void option_DumpOtpAndSeeprom(void);
|
||||
static void option_StartWupserver(void);
|
||||
static void option_LoadNetConf(void);
|
||||
static void option_displayDRCPin(void);
|
||||
static void option_InstallWUP(void);
|
||||
static void option_Shutdown(void);
|
||||
|
||||
extern int ppcHeartBeatThreadId;
|
||||
extern uint64_t currentColdbootOS;
|
||||
extern uint64_t currentColdbootTitle;
|
||||
|
||||
static int fsaHandle = -1;
|
||||
|
||||
static struct {
|
||||
const char* name;
|
||||
void (*callback)(void);
|
||||
} mainMenuOptions[] = {
|
||||
{ "Set Coldboot Title", option_SetColdbootTitle },
|
||||
{ "Dump Syslogs", option_DumpSyslogs },
|
||||
{ "Dump OTP + SEEPROM", option_DumpOtpAndSeeprom },
|
||||
{ "Start wupserver", option_StartWupserver },
|
||||
{ "Load Network Configuration", option_LoadNetConf },
|
||||
{ "Display DRC Pin", option_displayDRCPin },
|
||||
{ "Install WUP", option_InstallWUP, },
|
||||
{ "Shutdown", option_Shutdown },
|
||||
};
|
||||
static const int numMainMenuOptions = sizeof(mainMenuOptions) / sizeof(mainMenuOptions[0]);
|
||||
|
||||
static void waitButtonInput(void)
|
||||
{
|
||||
gfx_set_font_color(COLOR_PRIMARY);
|
||||
gfx_draw_rect_filled(8, SCREEN_HEIGHT - (16 + 8 + 2), SCREEN_WIDTH - 8 * 2, 2, COLOR_SECONDARY);
|
||||
gfx_printf(16, SCREEN_HEIGHT - 16, 0, "Press EJECT or POWER to proceed");
|
||||
|
||||
uint8_t cur_flag = 0;
|
||||
uint8_t flag = 0;
|
||||
|
||||
while (1) {
|
||||
readSystemEventFlag(&flag);
|
||||
if (cur_flag != flag) {
|
||||
if ((flag & SYSTEM_EVENT_FLAG_EJECT_BUTTON) || (flag & SYSTEM_EVENT_FLAG_POWER_BUTTON)) {
|
||||
return;
|
||||
}
|
||||
|
||||
cur_flag = flag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void option_SetColdbootTitle(void)
|
||||
{
|
||||
static const char* coldbootTitleOptions[] = {
|
||||
"Back",
|
||||
"Wii U Menu (JPN) - 00050010-10040000",
|
||||
"Wii U Menu (USA) - 00050010-10040100",
|
||||
"Wii U Menu (EUR) - 00050010-10040200",
|
||||
};
|
||||
|
||||
int rval;
|
||||
uint64_t newtid = 0;
|
||||
|
||||
int redraw = 1;
|
||||
int selected = 0;
|
||||
|
||||
uint8_t cur_flag = 0;
|
||||
uint8_t flag = 0;
|
||||
while (1) {
|
||||
readSystemEventFlag(&flag);
|
||||
if (cur_flag != flag) {
|
||||
if (flag & SYSTEM_EVENT_FLAG_EJECT_BUTTON) {
|
||||
selected++;
|
||||
|
||||
if (selected == 4) {
|
||||
selected = 0;
|
||||
}
|
||||
|
||||
redraw = 1;
|
||||
} else if (flag & SYSTEM_EVENT_FLAG_POWER_BUTTON) {
|
||||
switch (selected) {
|
||||
case 0:
|
||||
return;
|
||||
case 1:
|
||||
newtid = 0x0005001010040000llu;
|
||||
rval = setDefaultTitleId(newtid);
|
||||
break;
|
||||
case 2:
|
||||
newtid = 0x0005001010040100llu;
|
||||
rval = setDefaultTitleId(newtid);
|
||||
break;
|
||||
case 3:
|
||||
newtid = 0x0005001010040200llu;
|
||||
rval = setDefaultTitleId(newtid);
|
||||
break;
|
||||
}
|
||||
|
||||
redraw = 1;
|
||||
}
|
||||
|
||||
cur_flag = flag;
|
||||
}
|
||||
|
||||
if (redraw) {
|
||||
gfx_clear(COLOR_BACKGROUND);
|
||||
uint32_t index = 16 + 8 + 2 + 8;
|
||||
|
||||
// draw current titles
|
||||
gfx_printf(16, index, 0, "Current coldboot title: 0x%016llx", currentColdbootTitle);
|
||||
index += 8 + 4;
|
||||
|
||||
gfx_printf(16, index, 0, "Current coldboot os: 0x%016llx", currentColdbootOS);
|
||||
index += (8 + 4) * 2;
|
||||
|
||||
// draw options
|
||||
for (int i = 0; i < 4; i++) {
|
||||
gfx_draw_rect_filled(16 - 1, index - 1,
|
||||
gfx_get_text_width(coldbootTitleOptions[i]) + 2, 8 + 2,
|
||||
selected == i ? COLOR_PRIMARY : COLOR_BACKGROUND);
|
||||
|
||||
gfx_set_font_color(selected == i ? COLOR_BACKGROUND : COLOR_PRIMARY);
|
||||
gfx_printf(16, index, 0, coldbootTitleOptions[i]);
|
||||
|
||||
index += 8 + 4;
|
||||
}
|
||||
|
||||
gfx_set_font_color(COLOR_PRIMARY);
|
||||
|
||||
if (newtid) {
|
||||
index += (8 + 4) * 2;
|
||||
gfx_printf(16, index, 0, "Setting coldboot title id to 0x%016llx, rval %d", newtid, rval);
|
||||
index += 8 + 4;
|
||||
if (rval < 0) {
|
||||
gfx_set_font_color(COLOR_ERROR);
|
||||
gfx_printf(16, index, 0, "Error! Make sure title is installed correctly.");
|
||||
} else {
|
||||
gfx_set_font_color(COLOR_SUCCESS);
|
||||
gfx_printf(16, index, 0, "Success!");
|
||||
}
|
||||
}
|
||||
|
||||
// draw top bar
|
||||
gfx_set_font_color(COLOR_PRIMARY);
|
||||
const char* title = "Set Coldboot Title";
|
||||
gfx_printf((SCREEN_WIDTH / 2) + (gfx_get_text_width(title) / 2), 8, 1, title);
|
||||
gfx_draw_rect_filled(8, 16 + 8, SCREEN_WIDTH - 8 * 2, 2, COLOR_SECONDARY);
|
||||
|
||||
// draw bottom bar
|
||||
gfx_draw_rect_filled(8, SCREEN_HEIGHT - (16 + 8 + 2), SCREEN_WIDTH - 8 * 2, 2, COLOR_SECONDARY);
|
||||
gfx_printf(16, SCREEN_HEIGHT - 16, 0, "EJECT: Navigate ");
|
||||
gfx_printf(SCREEN_WIDTH - 16, SCREEN_HEIGHT - 16, 1, "POWER: Choose");
|
||||
|
||||
redraw = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void option_DumpSyslogs(void)
|
||||
{
|
||||
gfx_clear(COLOR_BACKGROUND);
|
||||
|
||||
// draw top bar
|
||||
gfx_set_font_color(COLOR_PRIMARY);
|
||||
const char* title = "Dumping Syslogs...";
|
||||
gfx_printf((SCREEN_WIDTH / 2) + (gfx_get_text_width(title) / 2), 8, 1, title);
|
||||
gfx_draw_rect_filled(8, 16 + 8, SCREEN_WIDTH - 8 * 2, 2, COLOR_SECONDARY);
|
||||
|
||||
uint32_t index = 16 + 8 + 2 + 8;
|
||||
gfx_printf(16, index, 0, "Creating 'logs' directory...");
|
||||
index += 8 + 4;
|
||||
|
||||
int res = FSA_MakeDir(fsaHandle, "/vol/storage_recovsd/logs", 0x600);
|
||||
if ((res < 0) && !(res == -0x30016)) {
|
||||
gfx_set_font_color(COLOR_ERROR);
|
||||
gfx_printf(16, index, 0, "Failed to create directory: %x", res);
|
||||
waitButtonInput();
|
||||
return;
|
||||
}
|
||||
|
||||
gfx_printf(16, index, 0, "Opening system 'logs' directory...");
|
||||
index += 8 + 4;
|
||||
|
||||
int dir_handle;
|
||||
res = FSA_OpenDir(fsaHandle, "/vol/system/logs", &dir_handle);
|
||||
if (res < 0) {
|
||||
gfx_set_font_color(COLOR_ERROR);
|
||||
gfx_printf(16, index, 0, "Failed to open system logs: %x", res);
|
||||
waitButtonInput();
|
||||
return;
|
||||
}
|
||||
|
||||
char src_path[500];
|
||||
char dst_path[500];
|
||||
FSDirectoryEntry dir_entry;
|
||||
while (FSA_ReadDir(fsaHandle, dir_handle, &dir_entry) >= 0) {
|
||||
if (dir_entry.stat.flags & DIR_ENTRY_IS_DIRECTORY) {
|
||||
continue;
|
||||
}
|
||||
|
||||
gfx_draw_rect_filled(0, index, SCREEN_WIDTH, 8, COLOR_BACKGROUND);
|
||||
gfx_printf(16, index, 0, "Copying %s...", dir_entry.name);
|
||||
|
||||
snprintf(src_path, sizeof(src_path), "/vol/system/logs/" "%s", dir_entry.name);
|
||||
snprintf(dst_path, sizeof(dst_path), "/vol/storage_recovsd/logs/" "%s", dir_entry.name);
|
||||
|
||||
res = copy_file(fsaHandle, src_path, dst_path);
|
||||
if (res < 0) {
|
||||
index += 8 + 4;
|
||||
gfx_set_font_color(COLOR_ERROR);
|
||||
gfx_printf(16, index, 0, "Failed to copy %s: %x", dir_entry.name, res);
|
||||
waitButtonInput();
|
||||
|
||||
FSA_CloseDir(fsaHandle, dir_handle);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
index += 8 + 4;
|
||||
gfx_set_font_color(COLOR_SUCCESS);
|
||||
gfx_printf(16, index, 0, "Done!");
|
||||
waitButtonInput();
|
||||
|
||||
FSA_CloseDir(fsaHandle, dir_handle);
|
||||
}
|
||||
|
||||
static void option_DumpOtpAndSeeprom(void)
|
||||
{
|
||||
gfx_clear(COLOR_BACKGROUND);
|
||||
|
||||
// draw top bar
|
||||
gfx_set_font_color(COLOR_PRIMARY);
|
||||
const char* title = "Dumping OTP + SEEPROM...";
|
||||
gfx_printf((SCREEN_WIDTH / 2) + (gfx_get_text_width(title) / 2), 8, 1, title);
|
||||
gfx_draw_rect_filled(8, 16 + 8, SCREEN_WIDTH - 8 * 2, 2, COLOR_SECONDARY);
|
||||
|
||||
uint32_t index = 16 + 8 + 2 + 8;
|
||||
gfx_printf(16, index, 0, "Creating otp.bin...");
|
||||
index += 8 + 4;
|
||||
|
||||
void* dataBuffer = IOS_HeapAllocAligned(0xcaff, 0x400, 0x40);
|
||||
if (!dataBuffer) {
|
||||
gfx_set_font_color(COLOR_ERROR);
|
||||
gfx_printf(16, index, 0, "Out of memory!");
|
||||
waitButtonInput();
|
||||
return;
|
||||
}
|
||||
|
||||
int otpHandle;
|
||||
int res = FSA_OpenFile(fsaHandle, "/vol/storage_recovsd/otp.bin", "w", &otpHandle);
|
||||
if (res < 0) {
|
||||
gfx_set_font_color(COLOR_ERROR);
|
||||
gfx_printf(16, index, 0, "Failed to create otp.bin: %x", res);
|
||||
waitButtonInput();
|
||||
|
||||
IOS_HeapFree(0xcaff, dataBuffer);
|
||||
return;
|
||||
}
|
||||
|
||||
gfx_printf(16, index, 0, "Reading OTP...");
|
||||
index += 8 + 4;
|
||||
|
||||
res = readOTP(dataBuffer, 0x400);
|
||||
if (res < 0) {
|
||||
gfx_set_font_color(COLOR_ERROR);
|
||||
gfx_printf(16, index, 0, "Failed to read OTP: %x", res);
|
||||
waitButtonInput();
|
||||
|
||||
FSA_CloseFile(fsaHandle, otpHandle);
|
||||
IOS_HeapFree(0xcaff, dataBuffer);
|
||||
return;
|
||||
}
|
||||
|
||||
gfx_printf(16, index, 0, "Writing otp.bin...");
|
||||
index += 8 + 4;
|
||||
|
||||
res = FSA_WriteFile(fsaHandle, dataBuffer, 1, 0x400, otpHandle, 0);
|
||||
if (res != 0x400) {
|
||||
gfx_set_font_color(COLOR_ERROR);
|
||||
gfx_printf(16, index, 0, "Failed to write otp.bin: %x", res);
|
||||
waitButtonInput();
|
||||
|
||||
FSA_CloseFile(fsaHandle, otpHandle);
|
||||
IOS_HeapFree(0xcaff, dataBuffer);
|
||||
return;
|
||||
}
|
||||
|
||||
FSA_CloseFile(fsaHandle, otpHandle);
|
||||
|
||||
gfx_printf(16, index, 0, "Creating seeprom.bin...");
|
||||
index += 8 + 4;
|
||||
|
||||
int seepromHandle;
|
||||
res = FSA_OpenFile(fsaHandle, "/vol/storage_recovsd/seeprom.bin", "w", &seepromHandle);
|
||||
if (res < 0) {
|
||||
gfx_set_font_color(COLOR_ERROR);
|
||||
gfx_printf(16, index, 0, "Failed to create seeprom.bin: %x", res);
|
||||
waitButtonInput();
|
||||
|
||||
IOS_HeapFree(0xcaff, dataBuffer);
|
||||
return;
|
||||
}
|
||||
|
||||
gfx_printf(16, index, 0, "Reading SEEPROM...");
|
||||
index += 8 + 4;
|
||||
|
||||
res = EEPROM_Read(0, 0x100, (uint16_t*) dataBuffer);
|
||||
if (res < 0) {
|
||||
gfx_set_font_color(COLOR_ERROR);
|
||||
gfx_printf(16, index, 0, "Failed to read EEPROM: %x", res);
|
||||
waitButtonInput();
|
||||
|
||||
FSA_CloseFile(fsaHandle, seepromHandle);
|
||||
IOS_HeapFree(0xcaff, dataBuffer);
|
||||
return;
|
||||
}
|
||||
|
||||
gfx_printf(16, index, 0, "Writing seeprom.bin...");
|
||||
index += 8 + 4;
|
||||
|
||||
res = FSA_WriteFile(fsaHandle, dataBuffer, 1, 0x200, seepromHandle, 0);
|
||||
if (res != 0x200) {
|
||||
gfx_set_font_color(COLOR_ERROR);
|
||||
gfx_printf(16, index, 0, "Failed to write seeprom.bin: %x", res);
|
||||
waitButtonInput();
|
||||
|
||||
FSA_CloseFile(fsaHandle, seepromHandle);
|
||||
IOS_HeapFree(0xcaff, dataBuffer);
|
||||
return;
|
||||
}
|
||||
|
||||
gfx_set_font_color(COLOR_SUCCESS);
|
||||
gfx_printf(16, index, 0, "Done!");
|
||||
waitButtonInput();
|
||||
|
||||
FSA_CloseFile(fsaHandle, seepromHandle);
|
||||
IOS_HeapFree(0xcaff, dataBuffer);
|
||||
}
|
||||
|
||||
static void option_StartWupserver(void)
|
||||
{
|
||||
gfx_clear(COLOR_BACKGROUND);
|
||||
|
||||
// draw top bar
|
||||
gfx_set_font_color(COLOR_PRIMARY);
|
||||
const char* title = "Running wupserver...";
|
||||
gfx_printf((SCREEN_WIDTH / 2) + (gfx_get_text_width(title) / 2), 8, 1, title);
|
||||
gfx_draw_rect_filled(8, 16 + 8, SCREEN_WIDTH - 8 * 2, 2, COLOR_SECONDARY);
|
||||
|
||||
uint32_t index = 16 + 8 + 2 + 8;
|
||||
gfx_printf(16, index, 0, "Initializing netconf...");
|
||||
index += 8 + 4;
|
||||
|
||||
int res = netconf_init();
|
||||
if (res <= 0) {
|
||||
gfx_set_font_color(COLOR_ERROR);
|
||||
gfx_printf(16, index, 0, "Failed to initialize netconf: %x", res);
|
||||
waitButtonInput();
|
||||
return;
|
||||
}
|
||||
|
||||
gfx_printf(16, index, 0, "Waiting for network connection... 5s");
|
||||
|
||||
NetConfInterfaceTypeEnum interface = 0xff;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
if (netconf_get_if_linkstate(NET_CFG_INTERFACE_TYPE_WIFI) == NET_CFG_LINK_STATE_UP) {
|
||||
interface = NET_CFG_INTERFACE_TYPE_WIFI;
|
||||
break;
|
||||
}
|
||||
|
||||
if (netconf_get_if_linkstate(NET_CFG_INTERFACE_TYPE_ETHERNET) == NET_CFG_LINK_STATE_UP) {
|
||||
interface = NET_CFG_INTERFACE_TYPE_ETHERNET;
|
||||
break;
|
||||
}
|
||||
|
||||
usleep(1000 * 1000);
|
||||
gfx_draw_rect_filled(0, index, SCREEN_WIDTH, 8, COLOR_BACKGROUND);
|
||||
gfx_printf(16, index, 0, "Waiting for network connection... %ds", 4 - i);
|
||||
}
|
||||
|
||||
index += 8 + 4;
|
||||
|
||||
if (interface == 0xff) {
|
||||
gfx_set_font_color(COLOR_ERROR);
|
||||
gfx_printf(16, index, 0, "No network connection!");
|
||||
waitButtonInput();
|
||||
return;
|
||||
}
|
||||
|
||||
gfx_printf(16, index, 0, "Connected using %s", (interface == NET_CFG_INTERFACE_TYPE_WIFI) ? "WIFI" : "ETHERNET");
|
||||
index += 8 + 4;
|
||||
|
||||
uint8_t ip_address[4];
|
||||
res = netconf_get_assigned_address(interface, (uint32_t*) ip_address);
|
||||
if (res < 0) {
|
||||
gfx_set_font_color(COLOR_ERROR);
|
||||
gfx_printf(16, index, 0, "Failed to get IP address: %x", res);
|
||||
waitButtonInput();
|
||||
return;
|
||||
}
|
||||
|
||||
gfx_printf(16, index, 0, "IP address: %d.%d.%d.%d", ip_address[0], ip_address[1], ip_address[2], ip_address[3]);
|
||||
index += 8 + 4;
|
||||
|
||||
res = socketInit();
|
||||
if (res <= 0) {
|
||||
gfx_set_font_color(COLOR_ERROR);
|
||||
gfx_printf(16, index, 0, "Failed to initialize socketlib: %x", res);
|
||||
waitButtonInput();
|
||||
return;
|
||||
}
|
||||
|
||||
wupserver_init();
|
||||
|
||||
gfx_set_font_color(COLOR_SUCCESS);
|
||||
gfx_printf(16, index, 0, "Wupserver running. Press EJECT or POWER to stop.");
|
||||
index += 8 + 4;
|
||||
|
||||
waitButtonInput();
|
||||
|
||||
gfx_set_font_color(COLOR_PRIMARY);
|
||||
gfx_printf(16, index, 0, "Stopping wupserver...");
|
||||
index += 8 + 4;
|
||||
|
||||
wupserver_deinit();
|
||||
}
|
||||
|
||||
static void network_parse_config_value(uint32_t* console_idx, NetConfCfg* cfg, const char* key, const char* value, uint32_t value_len)
|
||||
{
|
||||
if (strncmp(key, "type", sizeof("type")) == 0) {
|
||||
gfx_printf(16, *console_idx, 0, "Type: %s", value);
|
||||
(*console_idx) += 8 + 4;
|
||||
|
||||
if (value) {
|
||||
if (strncmp(value, "wifi", sizeof("wifi")) == 0) {
|
||||
cfg->wl0.if_index = NET_CFG_INTERFACE_TYPE_WIFI;
|
||||
cfg->wl0.if_sate = 1;
|
||||
cfg->wl0.ipv4Info.mode = NET_CONFIG_IPV4_MODE_AUTO_OBTAIN_IP;
|
||||
cfg->wifi.config_method = 0;
|
||||
} else if (strncmp(value, "eth", sizeof("eth")) == 0) {
|
||||
cfg->eth0.if_index = NET_CFG_INTERFACE_TYPE_ETHERNET;
|
||||
cfg->eth0.if_sate = 1;
|
||||
cfg->eth0.ipv4Info.mode = NET_CONFIG_IPV4_MODE_AUTO_OBTAIN_IP;
|
||||
cfg->ethCfg.negotiation = NET_CFG_ETH_CFG_NEGOTIATION_AUTO;
|
||||
}
|
||||
}
|
||||
} else if (strncmp(key, "ssid", sizeof("ssid")) == 0) {
|
||||
gfx_printf(16, *console_idx, 0, "SSID: %s (%d)", value, value_len);
|
||||
(*console_idx) += 8 + 4;
|
||||
|
||||
if (value) {
|
||||
memcpy(cfg->wifi.config.ssid, value, value_len);
|
||||
cfg->wifi.config.ssidlength = value_len;
|
||||
}
|
||||
} else if (strncmp(key, "key", sizeof("key")) == 0) {
|
||||
gfx_printf(16, *console_idx, 0, "Key: ******* (%d)", value_len);
|
||||
(*console_idx) += 8 + 4;
|
||||
|
||||
if (value) {
|
||||
memcpy(cfg->wifi.config.privacy.aes_key, value, value_len);
|
||||
cfg->wifi.config.privacy.aes_key_len = value_len;
|
||||
}
|
||||
} else if (strncmp(key, "key_type", sizeof("key_type")) == 0) {
|
||||
gfx_printf(16, *console_idx, 0, "Key type: %s", value);
|
||||
(*console_idx) += 8 + 4;
|
||||
|
||||
if (value) {
|
||||
if (strncmp(value, "NONE", sizeof("NONE")) == 0) {
|
||||
cfg->wifi.config.privacy.mode = NET_CFG_WIFI_PRIVACY_MODE_NONE;
|
||||
} else if (strncmp(value, "WEP", sizeof("WEP")) == 0) {
|
||||
cfg->wifi.config.privacy.mode = NET_CFG_WIFI_PRIVACY_MODE_WEP;
|
||||
} else if (strncmp(value, "WPA2_PSK_TKIP", sizeof("WPA2_PSK_TKIP")) == 0) {
|
||||
cfg->wifi.config.privacy.mode = NET_CFG_WIFI_PRIVACY_MODE_WPA2_PSK_TKIP;
|
||||
} else if (strncmp(value, "WPA_PSK_TKIP", sizeof("WPA_PSK_TKIP")) == 0) {
|
||||
cfg->wifi.config.privacy.mode = NET_CFG_WIFI_PRIVACY_MODE_WPA_PSK_TKIP;
|
||||
} else if (strncmp(value, "WPA2_PSK_AES", sizeof("WPA2_PSK_AES")) == 0) {
|
||||
cfg->wifi.config.privacy.mode = NET_CFG_WIFI_PRIVACY_MODE_WPA2_PSK_AES;
|
||||
} else if (strncmp(value, "WPA_PSK_AES", sizeof("WPA_PSK_AES")) == 0) {
|
||||
cfg->wifi.config.privacy.mode = NET_CFG_WIFI_PRIVACY_MODE_WPA_PSK_AES;
|
||||
} else {
|
||||
gfx_printf(16, *console_idx, 0, "Unknown key type!");
|
||||
(*console_idx) += 8 + 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void option_LoadNetConf(void)
|
||||
{
|
||||
gfx_clear(COLOR_BACKGROUND);
|
||||
|
||||
// draw top bar
|
||||
gfx_set_font_color(COLOR_PRIMARY);
|
||||
const char* title = "Loading network configuration...";
|
||||
gfx_printf((SCREEN_WIDTH / 2) + (gfx_get_text_width(title) / 2), 8, 1, title);
|
||||
gfx_draw_rect_filled(8, 16 + 8, SCREEN_WIDTH - 8 * 2, 2, COLOR_SECONDARY);
|
||||
|
||||
uint32_t index = 16 + 8 + 2 + 8;
|
||||
gfx_printf(16, index, 0, "Initializing netconf...");
|
||||
index += 8 + 4;
|
||||
|
||||
int res = netconf_init();
|
||||
if (res <= 0) {
|
||||
gfx_set_font_color(COLOR_ERROR);
|
||||
gfx_printf(16, index, 0, "Failed to initialize netconf: %x", res);
|
||||
waitButtonInput();
|
||||
return;
|
||||
}
|
||||
|
||||
gfx_printf(16, index, 0, "Reading network.cfg...");
|
||||
index += 8 + 4;
|
||||
|
||||
int cfgHandle;
|
||||
res = FSA_OpenFile(fsaHandle, "/vol/storage_recovsd/network.cfg", "r", &cfgHandle);
|
||||
if (res < 0) {
|
||||
gfx_set_font_color(COLOR_ERROR);
|
||||
gfx_printf(16, index, 0, "Failed to open network.cfg: %x", res);
|
||||
waitButtonInput();
|
||||
return;
|
||||
}
|
||||
|
||||
FSStat stat;
|
||||
res = FSA_StatFile(fsaHandle, cfgHandle, &stat);
|
||||
if (res < 0) {
|
||||
gfx_set_font_color(COLOR_ERROR);
|
||||
gfx_printf(16, index, 0, "Failed to stat file: %x", res);
|
||||
waitButtonInput();
|
||||
|
||||
FSA_CloseFile(fsaHandle, cfgHandle);
|
||||
return;
|
||||
}
|
||||
|
||||
char* cfgBuffer = (char*) IOS_HeapAllocAligned(0xcaff, stat.size + 1, 0x40);
|
||||
if (!cfgBuffer) {
|
||||
gfx_set_font_color(COLOR_ERROR);
|
||||
gfx_printf(16, index, 0, "Out of memory!");
|
||||
waitButtonInput();
|
||||
|
||||
FSA_CloseFile(fsaHandle, cfgHandle);
|
||||
return;
|
||||
}
|
||||
|
||||
cfgBuffer[stat.size] = '\0';
|
||||
|
||||
res = FSA_ReadFile(fsaHandle, cfgBuffer, 1, stat.size, cfgHandle, 0);
|
||||
if (res != stat.size) {
|
||||
gfx_set_font_color(COLOR_ERROR);
|
||||
gfx_printf(16, index, 0, "Failed to read file: %x", res);
|
||||
waitButtonInput();
|
||||
|
||||
IOS_HeapFree(0xcaff, cfgBuffer);
|
||||
FSA_CloseFile(fsaHandle, cfgHandle);
|
||||
return;
|
||||
}
|
||||
|
||||
NetConfCfg cfg;
|
||||
memset(&cfg, 0, sizeof(cfg));
|
||||
|
||||
// parse network cfg file
|
||||
const char* keyPtr = cfgBuffer;
|
||||
const char* valuePtr = NULL;
|
||||
for (size_t i = 0; i < stat.size; i++) {
|
||||
if (cfgBuffer[i] == '=') {
|
||||
cfgBuffer[i] = '\0';
|
||||
|
||||
valuePtr = cfgBuffer + i + 1;
|
||||
} else if (cfgBuffer[i] == '\n') {
|
||||
cfgBuffer[i] = '\0';
|
||||
|
||||
network_parse_config_value(&index, &cfg, keyPtr, valuePtr, (cfgBuffer + i) - valuePtr);
|
||||
|
||||
keyPtr = cfgBuffer + i + 1;
|
||||
valuePtr = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// if valuePtr isn't NULL there is another option without a newline at the end
|
||||
if (valuePtr) {
|
||||
network_parse_config_value(&index, &cfg, keyPtr, valuePtr, (cfgBuffer + stat.size) - valuePtr);
|
||||
}
|
||||
|
||||
gfx_printf(16, index, 0, "Applying configuration...");
|
||||
index += 8 + 4;
|
||||
|
||||
res = netconf_set_running(&cfg);
|
||||
if (res < 0) {
|
||||
gfx_set_font_color(COLOR_ERROR);
|
||||
gfx_printf(16, index, 0, "Failed to apply configuration: %x", res);
|
||||
waitButtonInput();
|
||||
|
||||
IOS_HeapFree(0xcaff, cfgBuffer);
|
||||
FSA_CloseFile(fsaHandle, cfgHandle);
|
||||
return;
|
||||
}
|
||||
|
||||
gfx_set_font_color(COLOR_SUCCESS);
|
||||
gfx_printf(16, index, 0, "Done!");
|
||||
index += 8 + 4;
|
||||
|
||||
waitButtonInput();
|
||||
|
||||
IOS_HeapFree(0xcaff, cfgBuffer);
|
||||
FSA_CloseFile(fsaHandle, cfgHandle);
|
||||
}
|
||||
|
||||
static void option_displayDRCPin(void)
|
||||
{
|
||||
gfx_clear(COLOR_BACKGROUND);
|
||||
|
||||
// draw top bar
|
||||
gfx_set_font_color(COLOR_PRIMARY);
|
||||
const char* title = "Display DRC Pin";
|
||||
gfx_printf((SCREEN_WIDTH / 2) + (gfx_get_text_width(title) / 2), 8, 1, title);
|
||||
gfx_draw_rect_filled(8, 16 + 8, SCREEN_WIDTH - 8 * 2, 2, COLOR_SECONDARY);
|
||||
|
||||
uint32_t index = 16 + 8 + 2 + 8;
|
||||
gfx_printf(16, index, 0, "Reading DRH mac address...");
|
||||
index += 8 + 4;
|
||||
|
||||
int ccrHandle = IOS_Open("/dev/ccr_cdc", 0);
|
||||
if (ccrHandle < 0) {
|
||||
gfx_set_font_color(COLOR_ERROR);
|
||||
gfx_printf(16, index, 0, "Failed to open /dev/ccr_cdc: %x", ccrHandle);
|
||||
waitButtonInput();
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t pincode[4];
|
||||
int res = CCRSysGetPincode(ccrHandle, pincode);
|
||||
if (res < 0) {
|
||||
gfx_set_font_color(COLOR_ERROR);
|
||||
gfx_printf(16, index, 0, "Failed to get pincode: %x", res);
|
||||
waitButtonInput();
|
||||
|
||||
IOS_Close(ccrHandle);
|
||||
return;
|
||||
}
|
||||
|
||||
static const char* symbol_names[] = {
|
||||
"spade",
|
||||
"heart",
|
||||
"diamond",
|
||||
"clubs",
|
||||
};
|
||||
|
||||
gfx_set_font_color(COLOR_SUCCESS);
|
||||
gfx_printf(16, index, 0, "Pincode: %x%x%x%x (%s %s %s %s)",
|
||||
pincode[0], pincode[1], pincode[2], pincode[3],
|
||||
symbol_names[pincode[0]], symbol_names[pincode[1]], symbol_names[pincode[2]], symbol_names[pincode[3]]);
|
||||
|
||||
waitButtonInput();
|
||||
|
||||
IOS_Close(ccrHandle);
|
||||
}
|
||||
|
||||
static void option_InstallWUP(void)
|
||||
{
|
||||
gfx_clear(COLOR_BACKGROUND);
|
||||
|
||||
// draw top bar
|
||||
gfx_set_font_color(COLOR_PRIMARY);
|
||||
const char* title = "Installing WUP";
|
||||
gfx_printf((SCREEN_WIDTH / 2) + (gfx_get_text_width(title) / 2), 8, 1, title);
|
||||
gfx_draw_rect_filled(8, 16 + 8, SCREEN_WIDTH - 8 * 2, 2, COLOR_SECONDARY);
|
||||
|
||||
uint32_t index = 16 + 8 + 2 + 8;
|
||||
gfx_printf(16, index, 0, "Make sure to place a valid signed WUP directly in 'sd:/install'");
|
||||
index += 8 + 4;
|
||||
|
||||
int mcpHandle = IOS_Open("/dev/mcp", 0);
|
||||
if (mcpHandle < 0) {
|
||||
gfx_set_font_color(COLOR_ERROR);
|
||||
gfx_printf(16, index, 0, "Failed to open /dev/mcp: %x", mcpHandle);
|
||||
waitButtonInput();
|
||||
return;
|
||||
}
|
||||
|
||||
gfx_printf(16, index, 0, "Querying install info...");
|
||||
index += 8 + 4;
|
||||
|
||||
MCPInstallInfo info;
|
||||
int res = MCP_InstallGetInfo(mcpHandle, "/vol/storage_recovsd/install", &info);
|
||||
if (res < 0) {
|
||||
gfx_set_font_color(COLOR_ERROR);
|
||||
gfx_printf(16, index, 0, "Failed to get install info: %x", res);
|
||||
waitButtonInput();
|
||||
|
||||
IOS_Close(mcpHandle);
|
||||
return;
|
||||
}
|
||||
|
||||
gfx_printf(16, index, 0, "Installing title: 0x%016llx...", info.titleId);
|
||||
index += 8 + 4;
|
||||
|
||||
// only install to NAND
|
||||
res = MCP_InstallSetTargetDevice(mcpHandle, 0);
|
||||
if (res < 0) {
|
||||
gfx_set_font_color(COLOR_ERROR);
|
||||
gfx_printf(16, index, 0, "MCP_InstallSetTargetDevice: %x", res);
|
||||
waitButtonInput();
|
||||
|
||||
IOS_Close(mcpHandle);
|
||||
return;
|
||||
}
|
||||
res = MCP_SetTargetUsb(mcpHandle, 0);
|
||||
if (res < 0) {
|
||||
gfx_set_font_color(COLOR_ERROR);
|
||||
gfx_printf(16, index, 0, "MCP_InstallSetTargetDevice: %x", res);
|
||||
waitButtonInput();
|
||||
|
||||
IOS_Close(mcpHandle);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: async installations
|
||||
res = MCP_InstallTitle(mcpHandle, "/vol/storage_recovsd/install");
|
||||
if (res < 0) {
|
||||
gfx_set_font_color(COLOR_ERROR);
|
||||
gfx_printf(16, index, 0, "Failed to install: %x", res);
|
||||
waitButtonInput();
|
||||
|
||||
IOS_Close(mcpHandle);
|
||||
return;
|
||||
}
|
||||
|
||||
gfx_set_font_color(COLOR_SUCCESS);
|
||||
gfx_printf(16, index, 0, "Done!");
|
||||
waitButtonInput();
|
||||
|
||||
IOS_Close(mcpHandle);
|
||||
}
|
||||
|
||||
static void option_Shutdown(void)
|
||||
{
|
||||
if (fsaHandle > 0) {
|
||||
// flush mlc and slc before forcing shutdown
|
||||
FSA_FlushVolume(fsaHandle, "/vol/storage_mlc01");
|
||||
FSA_FlushVolume(fsaHandle, "/vol/system");
|
||||
|
||||
// unmount sd
|
||||
FSA_Unmount(fsaHandle, "/vol/storage_recovsd", 2);
|
||||
|
||||
IOS_Close(fsaHandle);
|
||||
}
|
||||
|
||||
IOS_Shutdown(0);
|
||||
}
|
||||
|
||||
int menuThread(void* arg)
|
||||
{
|
||||
printf("menuThread running\n");
|
||||
|
||||
// stop ppcHeartbeatThread and reset PPC
|
||||
IOS_CancelThread(ppcHeartBeatThreadId, 0);
|
||||
resetPPC();
|
||||
|
||||
// open fsa and mount sdcard
|
||||
fsaHandle = IOS_Open("/dev/fsa", 0);
|
||||
if (fsaHandle > 0) {
|
||||
int res = FSA_Mount(fsaHandle, "/dev/sdcard01", "/vol/storage_recovsd", 2, NULL, 0);
|
||||
if (res < 0) {
|
||||
printf("Failed to mount SD: %x\n", res);
|
||||
}
|
||||
} else {
|
||||
printf("Failed to open FSA: %x\n", fsaHandle);
|
||||
}
|
||||
|
||||
int redraw = 1;
|
||||
int selected = 0;
|
||||
|
||||
uint8_t cur_flag = 0;
|
||||
uint8_t flag = 0;
|
||||
while (1) {
|
||||
readSystemEventFlag(&flag);
|
||||
if (cur_flag != flag) {
|
||||
if (flag & SYSTEM_EVENT_FLAG_EJECT_BUTTON) {
|
||||
selected++;
|
||||
|
||||
if (selected == numMainMenuOptions) {
|
||||
selected = 0;
|
||||
}
|
||||
|
||||
redraw = 1;
|
||||
} else if (flag & SYSTEM_EVENT_FLAG_POWER_BUTTON) {
|
||||
if (mainMenuOptions[selected].callback) {
|
||||
mainMenuOptions[selected].callback();
|
||||
redraw = 1;
|
||||
}
|
||||
}
|
||||
|
||||
cur_flag = flag;
|
||||
}
|
||||
|
||||
if (redraw) {
|
||||
gfx_clear(COLOR_BACKGROUND);
|
||||
|
||||
// draw options
|
||||
uint32_t index = 16 + 8 + 2 + 8;
|
||||
for (int i = 0; i < numMainMenuOptions; i++) {
|
||||
gfx_draw_rect_filled(16 - 1, index - 1,
|
||||
gfx_get_text_width(mainMenuOptions[i].name) + 2, 8 + 2,
|
||||
selected == i ? COLOR_PRIMARY : COLOR_BACKGROUND);
|
||||
|
||||
gfx_set_font_color(selected == i ? COLOR_BACKGROUND : COLOR_PRIMARY);
|
||||
gfx_printf(16, index, 0, mainMenuOptions[i].name);
|
||||
|
||||
index += 8 + 4;
|
||||
}
|
||||
|
||||
gfx_set_font_color(COLOR_PRIMARY);
|
||||
|
||||
// draw top bar
|
||||
gfx_set_font_color(COLOR_PRIMARY);
|
||||
const char* title = "Wii U Recovery Menu v0.1 by GaryOderNichts";
|
||||
gfx_printf((SCREEN_WIDTH / 2) + (gfx_get_text_width(title) / 2), 8, 1, title);
|
||||
gfx_draw_rect_filled(8, 16 + 8, SCREEN_WIDTH - 8 * 2, 2, COLOR_SECONDARY);
|
||||
|
||||
// draw bottom bar
|
||||
gfx_draw_rect_filled(8, SCREEN_HEIGHT - (16 + 8 + 2), SCREEN_WIDTH - 8 * 2, 2, COLOR_SECONDARY);
|
||||
gfx_printf(16, SCREEN_HEIGHT - 16, 0, "EJECT: Navigate ");
|
||||
gfx_printf(SCREEN_WIDTH - 16, SCREEN_HEIGHT - 16, 1, "POWER: Choose");
|
||||
|
||||
redraw = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
20
ios_mcp/source/menu.h
Normal file
20
ios_mcp/source/menu.h
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) 2022 GaryOderNichts
|
||||
*
|
||||
* 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, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
int menuThread(void* arg);
|
163
ios_mcp/source/netconf.c
Normal file
163
ios_mcp/source/netconf.c
Normal file
@ -0,0 +1,163 @@
|
||||
#include "netconf.h"
|
||||
#include "imports.h"
|
||||
#include <string.h>
|
||||
|
||||
static int ifmgr_handle = -1;
|
||||
|
||||
int netconf_init(void)
|
||||
{
|
||||
if(ifmgr_handle > 0) {
|
||||
return ifmgr_handle;
|
||||
}
|
||||
|
||||
int ret = IOS_Open("/dev/net/ifmgr/ncl", 0);
|
||||
if(ret > 0) {
|
||||
ifmgr_handle = ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int netconf_close(void)
|
||||
{
|
||||
int ret = IOS_Close(ifmgr_handle);
|
||||
if (ret >= 0) {
|
||||
ifmgr_handle = -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void* allocIobuf(uint32_t size)
|
||||
{
|
||||
void* ptr = IOS_HeapAlloc(0xcaff, size);
|
||||
|
||||
memset(ptr, 0x00, size);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void freeIobuf(void* ptr)
|
||||
{
|
||||
IOS_HeapFree(0xcaff, ptr);
|
||||
}
|
||||
|
||||
static int netconf_get_if_data(uint16_t* if_buf, uint16_t* data)
|
||||
{
|
||||
if (!data) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
return IOS_Ioctl(ifmgr_handle, 0x14, if_buf, 2, data, 8);
|
||||
}
|
||||
|
||||
NetConfLinkState netconf_get_if_linkstate(NetConfInterfaceType interface)
|
||||
{
|
||||
uint16_t* buf = (uint16_t*) allocIobuf(0xa);
|
||||
buf[0] = interface;
|
||||
|
||||
int res = netconf_get_if_data(buf, buf + 1);
|
||||
|
||||
freeIobuf(buf);
|
||||
|
||||
if (res >= 0) {
|
||||
return buf[2];
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int netconf_get_assigned_address(NetConfInterfaceType interface, uint32_t* assignedAddress)
|
||||
{
|
||||
uint16_t* buf = (uint16_t*) allocIobuf(8);
|
||||
buf[0] = interface;
|
||||
|
||||
int res = IOS_Ioctl(ifmgr_handle, 0x16, buf, 2, buf + 2, 4);
|
||||
if (res >= 0) {
|
||||
memcpy(assignedAddress, buf + 2, 4);
|
||||
}
|
||||
|
||||
freeIobuf(buf);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int netconf_set_if_admin_state(NetConfInterfaceType interface, uint16_t admin_state)
|
||||
{
|
||||
uint16_t* buf = (uint16_t*) allocIobuf(0x20);
|
||||
buf[0] = interface;
|
||||
buf[1] = admin_state;
|
||||
|
||||
int res = IOS_Ioctl(ifmgr_handle, 0xf, buf, 0x20, NULL, 0);
|
||||
|
||||
freeIobuf(buf);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int netconf_get_startup_profile_id(void)
|
||||
{
|
||||
return IOS_Ioctl(ifmgr_handle, 8, NULL, 0, NULL, 0);
|
||||
}
|
||||
|
||||
int netconf_nv_load(uint32_t profileId)
|
||||
{
|
||||
uint32_t* buf = allocIobuf(4);
|
||||
buf[0] = profileId;
|
||||
|
||||
int res = IOS_Ioctl(ifmgr_handle, 6, buf, 4, NULL, 0);
|
||||
|
||||
freeIobuf(buf);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int netconf_get_running(NetConfCfg* running)
|
||||
{
|
||||
uint32_t* buf = allocIobuf(0x280);
|
||||
|
||||
int res = IOS_Ioctl(ifmgr_handle, 4, NULL, 0, buf, 0x280);
|
||||
if (res >= 0) {
|
||||
memcpy(running, buf, 0x280);
|
||||
}
|
||||
|
||||
freeIobuf(buf);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int netconf_set_running(NetConfCfg* config)
|
||||
{
|
||||
uint32_t* buf = allocIobuf(0x280);
|
||||
memcpy(buf, config, 0x280);
|
||||
|
||||
int res = IOS_Ioctl(ifmgr_handle, 3, buf, 0x280, NULL, 0);
|
||||
|
||||
freeIobuf(buf);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int netconf_set_eth_cfg(NetConfEthCfg* config)
|
||||
{
|
||||
uint32_t* buf = allocIobuf(0x8);
|
||||
memcpy(buf, config, 0x8);
|
||||
|
||||
int res = IOS_Ioctl(ifmgr_handle, 0x1a, buf, 0x8, NULL, 0);
|
||||
|
||||
freeIobuf(buf);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int netconf_set_wifi_cfg(NetConfWifiConfig* config)
|
||||
{
|
||||
uint32_t* buf = allocIobuf(0x70);
|
||||
memcpy(buf, config, 0x70);
|
||||
|
||||
int res = IOS_Ioctl(ifmgr_handle, 0x11, buf, 0x70, NULL, 0);
|
||||
|
||||
freeIobuf(buf);
|
||||
|
||||
return res;
|
||||
}
|
188
ios_mcp/source/netconf.h
Normal file
188
ios_mcp/source/netconf.h
Normal file
@ -0,0 +1,188 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
|
||||
// Thanks @Maschell for all the types
|
||||
typedef uint16_t NetConfEthCfgSpeed;
|
||||
typedef uint16_t NetConfEthCfgDuplex;
|
||||
typedef uint16_t NetConfEthCfgNegotiation;
|
||||
typedef uint16_t NetConfWifiPrivacyMode;
|
||||
typedef uint16_t NetConfProxyAuthType;
|
||||
typedef uint16_t NetConfProxyStatus;
|
||||
typedef uint16_t NetConfInterfaceType;
|
||||
|
||||
typedef struct NetConfAllProfileState NetConfAllProfileState;
|
||||
typedef struct NetConfDNSInfo NetConfDNSInfo;
|
||||
typedef struct NetConfEthCfg NetConfEthCfg;
|
||||
typedef struct NetConfProxyConfig NetConfProxyConfig;
|
||||
typedef struct NetConfValidFlags NetConfValidFlags;
|
||||
typedef struct NetConfWifiConfig NetConfWifiConfig;
|
||||
typedef struct NetConfOpt NetConfOpt;
|
||||
typedef struct NetConfIPv4Info NetConfIPv4Info;
|
||||
typedef struct NetConfMACAddr NetConfMACAddr;
|
||||
typedef struct NetConfCfg NetConfCfg;
|
||||
typedef struct NetConfAOSSConfig NetConfAOSSConfig;
|
||||
typedef struct NetConfWifiConfigManualPrivacy NetConfWifiConfigManualPrivacy;
|
||||
typedef struct NetConfWifiConfigManual NetConfWifiConfigManual;
|
||||
typedef struct NetConfInterface NetConfInterface;
|
||||
|
||||
typedef enum NetConfInterfaceTypeEnum {
|
||||
NET_CFG_INTERFACE_TYPE_WIFI = 0,
|
||||
NET_CFG_INTERFACE_TYPE_ETHERNET = 1,
|
||||
}NetConfInterfaceTypeEnum;
|
||||
|
||||
typedef enum NetConfEthCfgSpeedEnum {
|
||||
NET_CFG_ETH_CFG_SPEED_10M = 10,
|
||||
NET_CFG_ETH_CFG_SPEED_100M = 100,
|
||||
} NetConfEthCfgSpeedEnum;
|
||||
|
||||
typedef enum NetConfEthCfgDuplexEnum {
|
||||
NET_CFG_ETH_CFG_DUPLEX_HALF = 1,
|
||||
NET_CFG_ETH_CFG_DUPLEX_FULL = 2,
|
||||
}NetConfEthCfgDuplexEnum;
|
||||
|
||||
typedef enum NetConfEthCfgNegotiationEnum {
|
||||
NET_CFG_ETH_CFG_NEGOTIATION_MANUAL = 1,
|
||||
NET_CFG_ETH_CFG_NEGOTIATION_AUTO = 2,
|
||||
}NetConfEthCfgNegotiationEnum;
|
||||
|
||||
typedef enum NetConfIPv4Mode {
|
||||
NET_CONFIG_IPV4_MODE_AUTO_OBTAIN_IP = 0,
|
||||
NET_CONFIG_IPV4_MODE_NO_AUTO_OBTAIN_IP = 2,
|
||||
}NetConfIPv4Mode;
|
||||
|
||||
typedef enum NetConfWifiPrivacyModeEnum {
|
||||
NET_CFG_WIFI_PRIVACY_MODE_NONE = 0,
|
||||
NET_CFG_WIFI_PRIVACY_MODE_WEP = 1,
|
||||
NET_CFG_WIFI_PRIVACY_MODE_WPA2_PSK_TKIP = 3,
|
||||
NET_CFG_WIFI_PRIVACY_MODE_WPA_PSK_TKIP = 4,
|
||||
NET_CFG_WIFI_PRIVACY_MODE_WPA2_PSK_AES = 5,
|
||||
NET_CFG_WIFI_PRIVACY_MODE_WPA_PSK_AES = 6,
|
||||
}NetConfWifiPrivacyModeEnum;
|
||||
|
||||
typedef enum NetConfProxyAuthTypeEnum {
|
||||
NET_CFG_PROXY_AUTH_TYPE_NONE = 0,
|
||||
NET_CFG_PROXY_AUTH_TYPE_BASIC_AUTHENTICATION = 1,
|
||||
}NetConfProxyAuthTypeEnum;
|
||||
|
||||
typedef enum NetConfProxyStatusEnum {
|
||||
NET_CFG_PROXY_DISABLED = 0,
|
||||
NET_CFG_PROXY_ENABLED = 1,
|
||||
}NetConfProxyStatusEnum;
|
||||
|
||||
typedef enum NetConfLinkState {
|
||||
NET_CFG_LINK_STATE_UP = 1,
|
||||
NET_CFG_LINK_STATE_NO_USED = 2,
|
||||
NET_CFG_LINK_STATE_DOWN = 3,
|
||||
}NetConfLinkState;
|
||||
|
||||
struct NetConfAllProfileState {
|
||||
char unk[0x18];
|
||||
};
|
||||
|
||||
struct NetConfDNSInfo {
|
||||
char unk[0xE];
|
||||
};
|
||||
|
||||
struct __attribute__((packed)) NetConfEthCfg {
|
||||
NetConfEthCfgSpeed speed;
|
||||
NetConfEthCfgDuplex duplex;
|
||||
NetConfEthCfgNegotiation negotiation;
|
||||
char padding[2];
|
||||
};
|
||||
|
||||
struct NetConfIPv4Info {
|
||||
NetConfIPv4Mode mode;
|
||||
uint32_t addr;
|
||||
uint32_t netmask;
|
||||
uint32_t nexthop; // gateway
|
||||
uint32_t ns1; // dns 1
|
||||
uint32_t ns2; // dns 2
|
||||
};
|
||||
|
||||
struct NetConfMACAddr {
|
||||
uint8_t MACAddr[0x6];
|
||||
};
|
||||
|
||||
struct NetConfProxyConfig {
|
||||
NetConfProxyStatus use_proxy; // true/false
|
||||
uint16_t port;
|
||||
char padding[2];
|
||||
NetConfProxyAuthType auth_type;
|
||||
char host[0x80];
|
||||
char username[0x80]; // only 0x20 bytes usable
|
||||
char password[0x40]; // only 0x20 bytes usable
|
||||
char noproxy_hosts[0x80]; // not used
|
||||
};
|
||||
|
||||
struct NetConfValidFlags {
|
||||
char unk[0x18];
|
||||
};
|
||||
|
||||
struct __attribute__((packed)) NetConfWifiConfigManualPrivacy {
|
||||
NetConfWifiPrivacyMode mode;
|
||||
char padding[2];
|
||||
uint16_t aes_key_len;
|
||||
uint8_t aes_key[0x40];
|
||||
char padding2[2];
|
||||
};
|
||||
|
||||
struct __attribute__((packed)) NetConfWifiConfigManual {
|
||||
uint8_t ssid[0x20];
|
||||
uint16_t ssidlength;
|
||||
char padding[2];
|
||||
NetConfWifiConfigManualPrivacy privacy;
|
||||
};
|
||||
|
||||
struct NetConfWifiConfig {
|
||||
uint16_t config_method;
|
||||
char padding[2];
|
||||
NetConfWifiConfigManual config;
|
||||
};
|
||||
|
||||
struct NetConfOpt {
|
||||
char unk[0x2c1];
|
||||
};
|
||||
|
||||
struct __attribute__((packed)) NetConfInterface {
|
||||
uint16_t if_index;
|
||||
uint16_t if_sate;
|
||||
uint32_t if_mtu;
|
||||
NetConfIPv4Info ipv4Info;
|
||||
};
|
||||
|
||||
struct NetConfCfg {
|
||||
NetConfInterface wl0;
|
||||
NetConfWifiConfig wifi;
|
||||
NetConfInterface eth0;
|
||||
NetConfEthCfg ethCfg;
|
||||
NetConfProxyConfig proxy;
|
||||
};
|
||||
static_assert(sizeof(NetConfCfg) == 0x280, "NetConfCfg: different size than expected");
|
||||
|
||||
struct NetConfAOSSConfig {
|
||||
char unk[0x1b0];
|
||||
};
|
||||
|
||||
int netconf_init(void);
|
||||
|
||||
int netconf_close(void);
|
||||
|
||||
NetConfLinkState netconf_get_if_linkstate(NetConfInterfaceType interface);
|
||||
|
||||
int netconf_get_assigned_address(NetConfInterfaceType interface, uint32_t* assignedAddress);
|
||||
|
||||
int netconf_set_if_admin_state(NetConfInterfaceType interface, uint16_t admin_state);
|
||||
|
||||
int netconf_get_startup_profile_id(void);
|
||||
|
||||
int netconf_nv_load(uint32_t profileId);
|
||||
|
||||
int netconf_get_running(NetConfCfg* running);
|
||||
|
||||
int netconf_set_running(NetConfCfg* config);
|
||||
|
||||
int netconf_set_eth_cfg(NetConfEthCfg* config);
|
||||
|
||||
int netconf_set_wifi_cfg(NetConfWifiConfig* config);
|
222
ios_mcp/source/socket.c
Normal file
222
ios_mcp/source/socket.c
Normal file
@ -0,0 +1,222 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "socket.h"
|
||||
#include "imports.h"
|
||||
|
||||
static int socket_handle = -1;
|
||||
|
||||
int socketInit()
|
||||
{
|
||||
if(socket_handle > 0) {
|
||||
return socket_handle;
|
||||
}
|
||||
|
||||
int ret = IOS_Open("/dev/socket", 0);
|
||||
if(ret > 0) {
|
||||
socket_handle = ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int socketExit()
|
||||
{
|
||||
int ret = IOS_Close(socket_handle);
|
||||
if (ret >= 0) {
|
||||
socket_handle = -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void* allocIobuf(uint32_t size)
|
||||
{
|
||||
void* ptr = IOS_HeapAlloc(0xcaff, size);
|
||||
|
||||
memset(ptr, 0x00, size);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void freeIobuf(void* ptr)
|
||||
{
|
||||
IOS_HeapFree(0xcaff, ptr);
|
||||
}
|
||||
|
||||
int socket(int domain, int type, int protocol)
|
||||
{
|
||||
uint8_t* iobuf = allocIobuf(0xC);
|
||||
uint32_t* inbuf = (uint32_t*)iobuf;
|
||||
|
||||
inbuf[0] = domain;
|
||||
inbuf[1] = type;
|
||||
inbuf[2] = protocol;
|
||||
|
||||
int ret = IOS_Ioctl(socket_handle, 0x11, inbuf, 0xC, NULL, 0);
|
||||
|
||||
freeIobuf(iobuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int closesocket(int sockfd)
|
||||
{
|
||||
uint8_t* iobuf = allocIobuf(0x4);
|
||||
uint32_t* inbuf = (uint32_t*)iobuf;
|
||||
|
||||
inbuf[0] = sockfd;
|
||||
|
||||
int ret = IOS_Ioctl(socket_handle, 0x3, inbuf, 0x4, NULL, 0);
|
||||
|
||||
freeIobuf(iobuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
|
||||
{
|
||||
uint8_t* iobuf = allocIobuf(0x18);
|
||||
uint32_t* inbuf = (uint32_t*)iobuf;
|
||||
uint32_t* outbuf = (uint32_t*)inbuf;
|
||||
|
||||
inbuf[0] = sockfd;
|
||||
|
||||
int ret = -1;
|
||||
|
||||
if(addr && addrlen && *addrlen == 0x10) {
|
||||
inbuf[5] = *addrlen;
|
||||
|
||||
ret = IOS_Ioctl(socket_handle, 0x1, inbuf, 0x18, outbuf, 0x18);
|
||||
if(ret >= 0) {
|
||||
memcpy(addr, &outbuf[1], outbuf[5]);
|
||||
*addrlen = outbuf[5];
|
||||
}
|
||||
} else {
|
||||
inbuf[5] = 0x10;
|
||||
|
||||
ret = IOS_Ioctl(socket_handle, 0x1, inbuf, 0x18, outbuf, 0x18);
|
||||
}
|
||||
|
||||
freeIobuf(iobuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
|
||||
{
|
||||
if(addrlen != 0x10) return -1;
|
||||
|
||||
uint8_t* iobuf = allocIobuf(0x18);
|
||||
uint32_t* inbuf = (uint32_t*)iobuf;
|
||||
|
||||
inbuf[0] = sockfd;
|
||||
memcpy(&inbuf[1], addr, addrlen);
|
||||
inbuf[5] = addrlen;
|
||||
|
||||
int ret = IOS_Ioctl(socket_handle, 0x2, inbuf, 0x18, NULL, 0);
|
||||
|
||||
freeIobuf(iobuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
|
||||
{
|
||||
if(addrlen != 0x10) return -1;
|
||||
|
||||
uint8_t* iobuf = allocIobuf(0x18);
|
||||
uint32_t* inbuf = (uint32_t*)iobuf;
|
||||
|
||||
inbuf[0] = sockfd;
|
||||
memcpy(&inbuf[1], addr, addrlen);
|
||||
inbuf[5] = addrlen;
|
||||
|
||||
int ret = IOS_Ioctl(socket_handle, 0x4, inbuf, 0x18, NULL, 0);
|
||||
|
||||
freeIobuf(iobuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int listen(int sockfd, int backlog)
|
||||
{
|
||||
uint8_t* iobuf = allocIobuf(0x8);
|
||||
uint32_t* inbuf = (uint32_t*)iobuf;
|
||||
|
||||
inbuf[0] = sockfd;
|
||||
inbuf[1] = backlog;
|
||||
|
||||
int ret = IOS_Ioctl(socket_handle, 0xA, inbuf, 0x8, NULL, 0);
|
||||
|
||||
freeIobuf(iobuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int shutdown(int sockfd, int how)
|
||||
{
|
||||
uint8_t* iobuf = allocIobuf(0x8);
|
||||
uint32_t* inbuf = (uint32_t*)iobuf;
|
||||
|
||||
inbuf[0] = sockfd;
|
||||
inbuf[1] = how;
|
||||
|
||||
int ret = IOS_Ioctl(socket_handle, 0x10, inbuf, 0x8, NULL, 0);
|
||||
|
||||
freeIobuf(iobuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t recv(int sockfd, void *buf, size_t len, int flags)
|
||||
{
|
||||
if(!len) return -101;
|
||||
|
||||
// TODO : size checks, split up data into multiple vectors if necessary
|
||||
void* data_buf = IOS_HeapAllocAligned(0xcaff, len, 0x40);
|
||||
if(!data_buf) return -100;
|
||||
|
||||
uint8_t* iobuf = allocIobuf(0x38);
|
||||
IOSVec_t* iovec = (IOSVec_t*)iobuf;
|
||||
uint32_t* inbuf = (uint32_t*)&iobuf[0x30];
|
||||
|
||||
inbuf[0] = sockfd;
|
||||
inbuf[1] = flags;
|
||||
|
||||
iovec[0].ptr = inbuf;
|
||||
iovec[0].len = 0x8;
|
||||
iovec[1].ptr = (void*)data_buf;
|
||||
iovec[1].len = len;
|
||||
|
||||
int ret = IOS_Ioctlv(socket_handle, 0xC, 1, 3, iovec);
|
||||
if(ret > 0 && buf) {
|
||||
memcpy(buf, data_buf, ret);
|
||||
}
|
||||
|
||||
freeIobuf(data_buf);
|
||||
freeIobuf(iobuf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t send(int sockfd, const void *buf, size_t len, int flags)
|
||||
{
|
||||
if(!buf || !len) return -101;
|
||||
|
||||
// TODO : size checks, split up data into multiple vectors if necessary
|
||||
void* data_buf = IOS_HeapAllocAligned(0xcaff, len, 0x40);
|
||||
if(!data_buf) return -100;
|
||||
|
||||
uint8_t* iobuf = allocIobuf(0x38);
|
||||
IOSVec_t* iovec = (IOSVec_t*)iobuf;
|
||||
uint32_t* inbuf = (uint32_t*)&iobuf[0x30];
|
||||
|
||||
memcpy(data_buf, buf, len);
|
||||
|
||||
inbuf[0] = sockfd;
|
||||
inbuf[1] = flags;
|
||||
|
||||
iovec[0].ptr = inbuf;
|
||||
iovec[0].len = 0x8;
|
||||
iovec[1].ptr = (void*)data_buf;
|
||||
iovec[1].len = len;
|
||||
|
||||
int ret = IOS_Ioctlv(socket_handle, 0xE, 4, 0, iovec);
|
||||
|
||||
freeIobuf(data_buf);
|
||||
freeIobuf(iobuf);
|
||||
return ret;
|
||||
}
|
123
ios_mcp/source/socket.h
Normal file
123
ios_mcp/source/socket.h
Normal file
@ -0,0 +1,123 @@
|
||||
#pragma once
|
||||
|
||||
// slightly stolen from ctrulib
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define SOL_SOCKET 0xFFFF
|
||||
|
||||
#define PF_UNSPEC 0
|
||||
#define PF_INET 2
|
||||
#define PF_INET6 10
|
||||
|
||||
#define AF_UNSPEC PF_UNSPEC
|
||||
#define AF_INET PF_INET
|
||||
#define AF_INET6 PF_INET6
|
||||
|
||||
#define SOCK_STREAM 1
|
||||
#define SOCK_DGRAM 2
|
||||
|
||||
#define MSG_CTRUNC 0x01000000
|
||||
#define MSG_DONTROUTE 0x02000000
|
||||
#define MSG_EOR 0x04000000
|
||||
#define MSG_OOB 0x08000000
|
||||
#define MSG_PEEK 0x10000000
|
||||
#define MSG_TRUNC 0x20000000
|
||||
#define MSG_WAITALL 0x40000000
|
||||
|
||||
#define SHUT_RD 0
|
||||
#define SHUT_WR 1
|
||||
#define SHUT_RDWR 2
|
||||
|
||||
#define SO_DEBUG 0x0001
|
||||
#define SO_ACCEPTCONN 0x0002
|
||||
#define SO_REUSEADDR 0x0004
|
||||
#define SO_KEEPALIVE 0x0008
|
||||
#define SO_DONTROUTE 0x0010
|
||||
#define SO_BROADCAST 0x0020
|
||||
#define SO_USELOOPBACK 0x0040
|
||||
#define SO_LINGER 0x0080
|
||||
#define SO_OOBINLINE 0x0100
|
||||
#define SO_REUSEPORT 0x0200
|
||||
#define SO_SNDBUF 0x1001
|
||||
#define SO_RCVBUF 0x1002
|
||||
#define SO_SNDLOWAT 0x1003
|
||||
#define SO_RCVLOWAT 0x1004
|
||||
#define SO_SNDTIMEO 0x1005
|
||||
#define SO_RCVTIMEO 0x1006
|
||||
#define SO_ERROR 0x1007
|
||||
#define SO_TYPE 0x1008
|
||||
|
||||
#define INADDR_ANY 0x00000000
|
||||
#define INADDR_BROADCAST 0xFFFFFFFF
|
||||
#define INADDR_NONE 0xFFFFFFFF
|
||||
|
||||
#define INET_ADDRSTRLEN 16
|
||||
|
||||
#define INADDR_LOOPBACK 0x7f000001
|
||||
#define INADDR_ANY 0x00000000
|
||||
#define INADDR_BROADCAST 0xFFFFFFFF
|
||||
#define INADDR_NONE 0xFFFFFFFF
|
||||
|
||||
#define INET_ADDRSTRLEN 16
|
||||
|
||||
#define IPPROTO_IP 0 /* dummy for IP */
|
||||
#define IPPROTO_UDP 17 /* user datagram protocol */
|
||||
#define IPPROTO_TCP 6 /* tcp */
|
||||
|
||||
#define IP_TOS 7
|
||||
#define IP_TTL 8
|
||||
#define IP_MULTICAST_LOOP 9
|
||||
#define IP_MULTICAST_TTL 10
|
||||
#define IP_ADD_MEMBERSHIP 11
|
||||
#define IP_DROP_MEMBERSHIP 12
|
||||
|
||||
typedef uint32_t socklen_t;
|
||||
typedef uint16_t sa_family_t;
|
||||
|
||||
struct sockaddr {
|
||||
sa_family_t sa_family;
|
||||
char sa_data[];
|
||||
};
|
||||
|
||||
struct sockaddr_storage {
|
||||
sa_family_t ss_family;
|
||||
char __ss_padding[14];
|
||||
};
|
||||
|
||||
typedef uint16_t in_port_t;
|
||||
typedef uint32_t in_addr_t;
|
||||
|
||||
struct in_addr {
|
||||
in_addr_t s_addr;
|
||||
};
|
||||
|
||||
struct sockaddr_in {
|
||||
sa_family_t sin_family;
|
||||
in_port_t sin_port;
|
||||
struct in_addr sin_addr;
|
||||
unsigned char sin_zero[8];
|
||||
};
|
||||
|
||||
struct linger {
|
||||
int l_onoff;
|
||||
int l_linger;
|
||||
};
|
||||
|
||||
int socketInit();
|
||||
int socketExit();
|
||||
|
||||
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
|
||||
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
|
||||
int closesocket(int sockfd);
|
||||
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
|
||||
int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
|
||||
int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
|
||||
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
|
||||
int listen(int sockfd, int backlog);
|
||||
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
|
||||
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
|
||||
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
|
||||
int shutdown(int sockfd, int how);
|
||||
int socket(int domain, int type, int protocol);
|
||||
int sockatmark(int sockfd);
|
92
ios_mcp/source/utils.c
Normal file
92
ios_mcp/source/utils.c
Normal file
@ -0,0 +1,92 @@
|
||||
#include "utils.h"
|
||||
#include "imports.h"
|
||||
#include "fsa.h"
|
||||
|
||||
#define COPY_BUFFER_SIZE 1024
|
||||
|
||||
#define HW_RSTB 0x0d800194
|
||||
|
||||
uint32_t kernRead32(uint32_t address)
|
||||
{
|
||||
return IOS_Syscall0x81(0, address, 0);
|
||||
}
|
||||
|
||||
void kernWrite32(uint32_t address, uint32_t value)
|
||||
{
|
||||
IOS_Syscall0x81(1, address, value);
|
||||
}
|
||||
|
||||
int readOTP(void* buf, uint32_t size)
|
||||
{
|
||||
return IOS_Syscall0x81(2, (uint32_t) buf, size);
|
||||
}
|
||||
|
||||
int EEPROM_Read(uint16_t offset, uint16_t num, uint16_t* buf)
|
||||
{
|
||||
if (offset + num > 0x100) {
|
||||
return -0x1d;
|
||||
}
|
||||
|
||||
for (uint16_t i = offset; i < offset + num; i++) {
|
||||
uint16_t tmp;
|
||||
int res = bspRead("EE", i, "access", 2, &tmp);
|
||||
if (res < 0) {
|
||||
return res;
|
||||
}
|
||||
|
||||
*buf = tmp;
|
||||
buf++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int resetPPC(void)
|
||||
{
|
||||
// cannot reset ppc through bsp?
|
||||
// uint8_t val = 1;
|
||||
// return bspWrite("PPC", 0, "Exe", 1, &val);
|
||||
|
||||
kernWrite32(HW_RSTB, kernRead32(HW_RSTB) & ~0x210);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int readSystemEventFlag(uint8_t* flag)
|
||||
{
|
||||
return bspRead("SMC", 0, "SystemEventFlag", 1, flag);
|
||||
}
|
||||
|
||||
int copy_file(int fsaFd, const char* src, const char* dst)
|
||||
{
|
||||
int readHandle;
|
||||
int res = FSA_OpenFile(fsaFd, src, "r", &readHandle);
|
||||
if (res < 0) {
|
||||
return res;
|
||||
}
|
||||
|
||||
int writeHandle;
|
||||
res = FSA_OpenFile(fsaFd, dst, "w", &writeHandle);
|
||||
if (res < 0) {
|
||||
FSA_CloseFile(fsaFd, readHandle);
|
||||
return res;
|
||||
}
|
||||
|
||||
void* dataBuffer = IOS_HeapAllocAligned(0xcaff, COPY_BUFFER_SIZE, 0x40);
|
||||
if (!dataBuffer) {
|
||||
FSA_CloseFile(fsaFd, readHandle);
|
||||
FSA_CloseFile(fsaFd, writeHandle);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while ((res = FSA_ReadFile(fsaFd, dataBuffer, 1, COPY_BUFFER_SIZE, readHandle, 0)) > 0) {
|
||||
if ((res = FSA_WriteFile(fsaFd, dataBuffer, 1, res, writeHandle, 0)) < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
IOS_HeapFree(0xcaff, dataBuffer);
|
||||
FSA_CloseFile(fsaFd, writeHandle);
|
||||
FSA_CloseFile(fsaFd, readHandle);
|
||||
|
||||
return (res > 0) ? 0 : res;
|
||||
}
|
24
ios_mcp/source/utils.h
Normal file
24
ios_mcp/source/utils.h
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
#define SYSTEM_EVENT_FLAG_WAKE1 0x01
|
||||
#define SYSTEM_EVENT_FLAG_WAKE0 0x02
|
||||
#define SYSTEM_EVENT_FLAG_BT_INTERRUPT 0x04
|
||||
#define SYSTEM_EVENT_FLAG_TIMER_SIGNAL 0x08
|
||||
#define SYSTEM_EVENT_FLAG_DISC_INSERT 0x10
|
||||
#define SYSTEM_EVENT_FLAG_EJECT_BUTTON 0x20
|
||||
#define SYSTEM_EVENT_FLAG_POWER_BUTTON 0x40
|
||||
|
||||
uint32_t kernRead32(uint32_t address);
|
||||
|
||||
void kernWrite32(uint32_t address, uint32_t value);
|
||||
|
||||
int readOTP(void* buf, uint32_t size);
|
||||
|
||||
int EEPROM_Read(uint16_t offset, uint16_t num, uint16_t* buf);
|
||||
|
||||
int resetPPC(void);
|
||||
|
||||
int readSystemEventFlag(uint8_t* flag);
|
||||
|
||||
int copy_file(int fsaFd, const char* src, const char* dst);
|
206
ios_mcp/source/wupserver.c
Normal file
206
ios_mcp/source/wupserver.c
Normal file
@ -0,0 +1,206 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include "imports.h"
|
||||
#include "socket.h"
|
||||
#include "wupserver.h"
|
||||
|
||||
#define MCP_SVC_BASE ((void*) 0x050567ec)
|
||||
|
||||
static int serverRunning = 0;
|
||||
static int serverSocket = -1;
|
||||
static int threadId = -1;
|
||||
static uint8_t threadStack[0x1000] __attribute__((aligned(0x20)));
|
||||
|
||||
// overwrites command_buffer with response
|
||||
// returns length of response (or 0 for no response, negative for error)
|
||||
static int serverCommandHandler(uint32_t* command_buffer, uint32_t length)
|
||||
{
|
||||
if(!command_buffer || !length) return -1;
|
||||
|
||||
int out_length = 4;
|
||||
|
||||
switch (command_buffer[0]) {
|
||||
case 0: {
|
||||
// write
|
||||
// [cmd_id][addr]
|
||||
void* dst = (void*)command_buffer[1];
|
||||
|
||||
memcpy(dst, &command_buffer[2], length - 8);
|
||||
}
|
||||
break;
|
||||
case 1: {
|
||||
// read
|
||||
// [cmd_id][addr][length]
|
||||
void* src = (void*)command_buffer[1];
|
||||
length = command_buffer[2];
|
||||
|
||||
memcpy(&command_buffer[1], src, length);
|
||||
out_length = length + 4;
|
||||
}
|
||||
break;
|
||||
case 2: {
|
||||
// svc
|
||||
// [cmd_id][svc_id]
|
||||
int svc_id = command_buffer[1];
|
||||
int size_arguments = length - 8;
|
||||
|
||||
uint32_t arguments[8];
|
||||
memset(arguments, 0x00, sizeof(arguments));
|
||||
memcpy(arguments, &command_buffer[2], (size_arguments < 8 * 4) ? size_arguments : (8 * 4));
|
||||
|
||||
// return error code as data
|
||||
out_length = 8;
|
||||
command_buffer[1] = ((int (*const)(uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t))(MCP_SVC_BASE + svc_id * 8))
|
||||
(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5], arguments[6], arguments[7]);
|
||||
}
|
||||
break;
|
||||
case 3: {
|
||||
// kill
|
||||
// [cmd_id]
|
||||
wupserver_deinit();
|
||||
}
|
||||
break;
|
||||
case 4: {
|
||||
// memcpy
|
||||
// [dst][src][size]
|
||||
void* dst = (void*)command_buffer[1];
|
||||
void* src = (void*)command_buffer[2];
|
||||
int size = command_buffer[3];
|
||||
|
||||
memcpy(dst, src, size);
|
||||
}
|
||||
break;
|
||||
case 5: {
|
||||
// repeated-write
|
||||
// [address][value][n]
|
||||
uint32_t* dst = (uint32_t*)command_buffer[1];
|
||||
uint32_t* cache_range = (uint32_t*)(command_buffer[1] & ~0xFF);
|
||||
uint32_t value = command_buffer[2];
|
||||
uint32_t n = command_buffer[3];
|
||||
|
||||
uint32_t old = *dst;
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (*dst != old) {
|
||||
if (*dst == 0x0) {
|
||||
old = *dst;
|
||||
} else {
|
||||
*dst = value;
|
||||
IOS_FlushDCache(cache_range, 0x100);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
IOS_InvalidateDCache(cache_range, 0x100);
|
||||
usleep(50);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// unknown command
|
||||
return -2;
|
||||
break;
|
||||
}
|
||||
|
||||
// no error !
|
||||
command_buffer[0] = 0;
|
||||
return out_length;
|
||||
}
|
||||
|
||||
static void serverClientHandler(int sock)
|
||||
{
|
||||
uint32_t command_buffer[0x180];
|
||||
|
||||
while (serverRunning) {
|
||||
int ret = recv(sock, command_buffer, sizeof(command_buffer), 0);
|
||||
if (ret <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
ret = serverCommandHandler(command_buffer, ret);
|
||||
if (ret > 0) {
|
||||
send(sock, command_buffer, ret, 0);
|
||||
} else if (ret < 0) {
|
||||
send(sock, &ret, sizeof(int), 0);
|
||||
}
|
||||
}
|
||||
|
||||
closesocket(sock);
|
||||
}
|
||||
|
||||
static void serverListenClients()
|
||||
{
|
||||
serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
|
||||
struct sockaddr_in server;
|
||||
memset(&server, 0, sizeof(server));
|
||||
|
||||
server.sin_family = AF_INET;
|
||||
server.sin_port = 1337;
|
||||
server.sin_addr.s_addr = 0;
|
||||
|
||||
if (bind(serverSocket, (struct sockaddr*) &server, sizeof(server)) < 0) {
|
||||
closesocket(serverSocket);
|
||||
serverSocket = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (listen(serverSocket, 1) < 0) {
|
||||
closesocket(serverSocket);
|
||||
serverSocket = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
while (serverRunning) {
|
||||
int csock = accept(serverSocket, NULL, NULL);
|
||||
if (csock < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
serverClientHandler(csock);
|
||||
}
|
||||
|
||||
closesocket(serverSocket);
|
||||
serverSocket = -1;
|
||||
}
|
||||
|
||||
static int wupserver_thread(void *arg)
|
||||
{
|
||||
while (1) {
|
||||
if (!serverRunning) {
|
||||
break;
|
||||
}
|
||||
|
||||
serverListenClients();
|
||||
usleep(1000 * 1000);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void wupserver_init(void)
|
||||
{
|
||||
if (!serverRunning) {
|
||||
serverSocket = -1;
|
||||
|
||||
threadId = IOS_CreateThread(wupserver_thread, NULL, threadStack + sizeof(threadStack), sizeof(threadStack), IOS_GetThreadPriority(0), 0);
|
||||
if(threadId >= 0) {
|
||||
IOS_StartThread(threadId);
|
||||
serverRunning = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void wupserver_deinit(void)
|
||||
{
|
||||
if (serverRunning) {
|
||||
serverRunning = 0;
|
||||
|
||||
if (serverSocket >= 0) {
|
||||
shutdown(serverSocket, SHUT_RDWR);
|
||||
}
|
||||
|
||||
IOS_JoinThread(threadId, NULL);
|
||||
}
|
||||
}
|
5
ios_mcp/source/wupserver.h
Normal file
5
ios_mcp/source/wupserver.h
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
void wupserver_init(void);
|
||||
|
||||
void wupserver_deinit(void);
|
BIN
screenshot.png
Normal file
BIN
screenshot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 123 KiB |
Loading…
Reference in New Issue
Block a user